blob: da6d957c543d1f1f8a787f54378f11252a094505 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.view.LayoutInflater;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
/**
* This class allows us to wrap the application context so that the WebView implementation can
* correctly reference both org.chromium.* and application classes which is necessary to properly
* inflate UI.
*/
public class ResourcesContextWrapperFactory {
private ResourcesContextWrapperFactory() {}
// Note WeakHashMap only guarantees that keys are weakly held, and ContextWrapper holds a strong
// reference to the wrapped context. So need a WeakReference here to ensure the Context does not
// leak.
private static final WeakHashMap<Context, WeakReference<WebViewContextWrapper>> sCtxToWrapper =
new WeakHashMap<>();
private static final Object sLock = new Object();
public static Context get(Context ctx) {
// Avoid double-wrapping a context.
if (ctx instanceof WebViewContextWrapper) {
return ctx;
}
WebViewContextWrapper wrapper = null;
synchronized (sLock) {
WeakReference<WebViewContextWrapper> ref = sCtxToWrapper.get(ctx);
wrapper = (ref == null) ? null : ref.get();
if (wrapper == null) {
wrapper = new WebViewContextWrapper(ctx);
sCtxToWrapper.put(ctx, new WeakReference<>(wrapper));
}
}
return wrapper;
}
private static class WebViewContextWrapper extends ContextWrapper {
private Context mApplicationContext;
public WebViewContextWrapper(Context base) {
super(base);
}
@Override
public ClassLoader getClassLoader() {
final ClassLoader appCl = getBaseContext().getClassLoader();
final ClassLoader webViewCl = this.getClass().getClassLoader();
return new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// First look in the WebViewProvider class loader.
try {
return webViewCl.loadClass(name);
} catch (ClassNotFoundException e) {
// Look in the app class loader; allowing it to throw
// ClassNotFoundException.
return appCl.loadClass(name);
}
}
};
}
@Override
public Object getSystemService(String name) {
if (Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
LayoutInflater i = (LayoutInflater) getBaseContext().getSystemService(name);
return i.cloneInContext(this);
} else {
return getBaseContext().getSystemService(name);
}
}
@Override
@SuppressWarnings("NoContextGetApplicationContext")
public Context getApplicationContext() {
if (mApplicationContext == null) {
Context appCtx = getBaseContext().getApplicationContext();
if (appCtx == getBaseContext()) {
mApplicationContext = this;
} else {
mApplicationContext = get(appCtx);
}
}
return mApplicationContext;
}
@Override
public void registerComponentCallbacks(ComponentCallbacks callback) {
// We have to override registerComponentCallbacks and unregisterComponentCallbacks
// since they call getApplicationContext().[un]registerComponentCallbacks()
// which causes us to go into a loop.
getBaseContext().registerComponentCallbacks(callback);
}
@Override
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
getBaseContext().unregisterComponentCallbacks(callback);
}
@Override
public void startActivity(Intent intent) {
if (AwContents.activityFromContext(this) == null) {
// FLAG_ACTIVITY_NEW_TASK is needed to start activities from a non-activity
// context.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
super.startActivity(intent);
}
}
}