diff options
18 files changed, 350 insertions, 332 deletions
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc index ea98457..958c66f 100644 --- a/android_webview/browser/aw_browser_context.cc +++ b/android_webview/browser/aw_browser_context.cc @@ -41,6 +41,8 @@ class AwResourceContext : public content::ResourceContext { DISALLOW_COPY_AND_ASSIGN(AwResourceContext); }; +AwBrowserContext* g_browser_context = NULL; + } // namespace AwBrowserContext::AwBrowserContext( @@ -48,9 +50,20 @@ AwBrowserContext::AwBrowserContext( JniDependencyFactory* native_factory) : context_storage_path_(path), native_factory_(native_factory) { + DCHECK(g_browser_context == NULL); + g_browser_context = this; } AwBrowserContext::~AwBrowserContext() { + DCHECK(g_browser_context == this); + g_browser_context = NULL; +} + +// static +AwBrowserContext* AwBrowserContext::GetDefault() { + // TODO(joth): rather than store in a global here, lookup this instance + // from the Java-side peer. + return g_browser_context; } // static diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h index b2f9178..b6a1134 100644 --- a/android_webview/browser/aw_browser_context.h +++ b/android_webview/browser/aw_browser_context.h @@ -46,6 +46,9 @@ class AwBrowserContext : public content::BrowserContext, JniDependencyFactory* native_factory); virtual ~AwBrowserContext(); + // Currently only one instance per process is supported. + static AwBrowserContext* GetDefault(); + // Convenience method to returns the AwBrowserContext corresponding to the // given WebContents. static AwBrowserContext* FromWebContents( diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index d41eccc..d58b8d7 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc @@ -29,8 +29,6 @@ namespace android_webview { namespace { -AwBrowserContext* g_browser_context; - class AwAccessTokenStore : public content::AccessTokenStore { public: AwAccessTokenStore() { } @@ -67,7 +65,7 @@ std::string AwContentBrowserClient::GetAcceptLangsImpl() { } AwBrowserContext* AwContentBrowserClient::GetAwBrowserContext() { - return g_browser_context; + return AwBrowserContext::GetDefault(); } AwContentBrowserClient::AwContentBrowserClient( @@ -79,11 +77,9 @@ AwContentBrowserClient::AwContentBrowserClient( } browser_context_.reset( new AwBrowserContext(user_data_dir, native_factory_)); - g_browser_context = browser_context_.get(); } AwContentBrowserClient::~AwContentBrowserClient() { - g_browser_context = NULL; } void AwContentBrowserClient::AddCertificate(net::URLRequest* request, diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h index 5724efa..1ecd2f5 100644 --- a/android_webview/browser/aw_content_browser_client.h +++ b/android_webview/browser/aw_content_browser_client.h @@ -21,7 +21,7 @@ class AwContentBrowserClient : public content::ContentBrowserClient { // This is what AwContentBrowserClient::GetAcceptLangs uses. static std::string GetAcceptLangsImpl(); - // Returns the one AwBrowserContext for this process. + // Deprecated: use AwBrowserContext::GetDefault() instead. static AwBrowserContext* GetAwBrowserContext(); AwContentBrowserClient(JniDependencyFactory* native_factory); diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h index e24b25c..7d05aee 100644 --- a/android_webview/browser/browser_view_renderer.h +++ b/android_webview/browser/browser_view_renderer.h @@ -78,9 +78,6 @@ class BrowserViewRenderer { static AwDrawSWFunctionTable* GetAwDrawSWFunctionTable(); static bool IsSkiaVersionCompatible(); - // Content control methods. - virtual void SetContents(content::ContentViewCore* content_view_core) = 0; - // Rendering methods. // Main handler for view drawing: performs a SW draw immediately, or sets up diff --git a/android_webview/browser/in_process_view_renderer.cc b/android_webview/browser/in_process_view_renderer.cc index 0ad4a41..e79591c 100644 --- a/android_webview/browser/in_process_view_renderer.cc +++ b/android_webview/browser/in_process_view_renderer.cc @@ -299,10 +299,11 @@ bool BrowserViewRenderer::IsSkiaVersionCompatible() { InProcessViewRenderer::InProcessViewRenderer( BrowserViewRenderer::Client* client, - JavaHelper* java_helper) + JavaHelper* java_helper, + content::WebContents* web_contents) : client_(client), java_helper_(java_helper), - web_contents_(NULL), + web_contents_(web_contents), compositor_(NULL), view_visible_(false), continuous_invalidate_(false), @@ -314,11 +315,18 @@ InProcessViewRenderer::InProcessViewRenderer( hardware_failed_(false), egl_context_at_init_(NULL), weak_factory_(this) { + CHECK(web_contents_); + web_contents_->SetUserData(kUserDataKey, new UserData(this)); + content::SynchronousCompositor::SetClientForWebContents(web_contents_, this); + // Currently the logic in this class relies on |compositor_| remaining NULL + // until the DidInitializeCompositor() call, hence it is not set here. } InProcessViewRenderer::~InProcessViewRenderer() { - SetContents(NULL); - DCHECK(compositor_ == NULL); + CHECK(web_contents_); + content::SynchronousCompositor::SetClientForWebContents(web_contents_, NULL); + web_contents_->SetUserData(kUserDataKey, NULL); + DCHECK(web_contents_ == NULL); // WebContentsGone should have been called. } // static @@ -327,26 +335,6 @@ InProcessViewRenderer* InProcessViewRenderer::FromWebContents( return UserData::GetInstance(contents); } -void InProcessViewRenderer::SetContents( - content::ContentViewCore* content_view_core) { - // First remove association from the prior ContentViewCore / WebContents. - if (web_contents_) { - content::SynchronousCompositor::SetClientForWebContents(web_contents_, - NULL); - web_contents_->SetUserData(kUserDataKey, NULL); - DCHECK(!web_contents_); // WebContentsGone should have been called. - } - - if (!content_view_core) - return; - - web_contents_ = content_view_core->GetWebContents(); - web_contents_->SetUserData(kUserDataKey, new UserData(this)); - content::SynchronousCompositor::SetClientForWebContents(web_contents_, this); - // Currently the logic in this class relies on |compositor_| remaining NULL - // until the DidInitializeCompositor() call, hence it is not set here. -} - void InProcessViewRenderer::WebContentsGone() { web_contents_ = NULL; compositor_ = NULL; diff --git a/android_webview/browser/in_process_view_renderer.h b/android_webview/browser/in_process_view_renderer.h index 55d5415b..d3e4166 100644 --- a/android_webview/browser/in_process_view_renderer.h +++ b/android_webview/browser/in_process_view_renderer.h @@ -26,15 +26,14 @@ class InProcessViewRenderer : public BrowserViewRenderer, public content::SynchronousCompositorClient { public: InProcessViewRenderer(BrowserViewRenderer::Client* client, - JavaHelper* java_helper); + JavaHelper* java_helper, + content::WebContents* web_contents); virtual ~InProcessViewRenderer(); static InProcessViewRenderer* FromWebContents( content::WebContents* contents); // BrowserViewRenderer overrides - virtual void SetContents( - content::ContentViewCore* content_view_core) OVERRIDE; virtual bool OnDraw(jobject java_canvas, bool is_hardware_canvas, const gfx::Point& scroll, diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java index 2536d7b..9b0c987 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java +++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java @@ -24,7 +24,14 @@ public class AwBrowserContext { private AwGeolocationPermissions mGeolocationPermissions; private AwCookieManager mCookieManager; private AwFormDatabase mFormDatabase; + boolean mIsFileAccessGrantedByDefault; + public AwBrowserContext(SharedPreferences sharedPreferences, + boolean isFileAccessGrantedByDefault) { + mIsFileAccessGrantedByDefault = isFileAccessGrantedByDefault; + } + + @Deprecated public AwBrowserContext(SharedPreferences sharedPreferences) { mSharedPreferences = sharedPreferences; } @@ -63,4 +70,4 @@ public class AwBrowserContext { public void resumeTimers() { ContentViewStatics.setWebKitSharedTimersSuspended(false); } -}
\ No newline at end of file +} diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 4730d24..9793b06 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -4,6 +4,7 @@ package org.chromium.android_webview; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -15,6 +16,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Message; +import android.os.Process; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; @@ -107,19 +109,20 @@ public class AwContents { } private int mNativeAwContents; - private AwBrowserContext mBrowserContext; - private ViewGroup mContainerView; + private final AwBrowserContext mBrowserContext; + private final ViewGroup mContainerView; private ContentViewCore mContentViewCore; - private AwContentsClient mContentsClient; - private AwContentsClientBridge mContentsClientBridge; - private AwWebContentsDelegate mWebContentsDelegate; - private AwContentsIoThreadClient mIoThreadClient; - private InterceptNavigationDelegateImpl mInterceptNavigationDelegate; - private InternalAccessDelegate mInternalAccessAdapter; + private final AwContentsClient mContentsClient; + private final AwContentsClientBridge mContentsClientBridge; + private final AwWebContentsDelegate mWebContentsDelegate; + private final AwContentsIoThreadClient mIoThreadClient; + private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate; + private final InternalAccessDelegate mInternalAccessAdapter; private final AwLayoutSizer mLayoutSizer; - private AwZoomControls mZoomControls; + private final AwZoomControls mZoomControls; // This can be accessed on any thread after construction. See AwContentsIoThreadClient. private final AwSettings mSettings; + private boolean mIsPaused; private Bitmap mFavicon; private boolean mHasRequestedVisitedHistoryFromClient; @@ -127,7 +130,7 @@ public class AwContents { private final double mDIPScale; // Must call nativeUpdateLastHitTestData first to update this before use. - private final HitTestData mPossiblyStaleHitTestData; + private final HitTestData mPossiblyStaleHitTestData = new HitTestData(); private DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler; @@ -313,15 +316,24 @@ public class AwContents { * @param containerView the view-hierarchy item this object will be bound to. * @param internalAccessAdapter to access private methods on containerView. * @param contentsClient will receive API callbacks from this WebView Contents - * @param isAccessFromFileURLsGrantedByDefault passed to AwSettings. * * This constructor uses the default view sizing policy. */ public AwContents(AwBrowserContext browserContext, ViewGroup containerView, + InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient) { + this(browserContext, containerView, internalAccessAdapter, contentsClient, + new AwLayoutSizer()); + } + + // TODO(joth): Remove this overload when downstream no-longer passing + // isAccessFromFileURLsGrantedByDefault. + @Deprecated + public AwContents(AwBrowserContext browserContext, ViewGroup containerView, InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, boolean isAccessFromFileURLsGrantedByDefault) { this(browserContext, containerView, internalAccessAdapter, contentsClient, - isAccessFromFileURLsGrantedByDefault, new AwLayoutSizer()); + new AwLayoutSizer()); + browserContext.mIsFileAccessGrantedByDefault = isAccessFromFileURLsGrantedByDefault; } private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView, @@ -348,96 +360,130 @@ public class AwContents { */ public AwContents(AwBrowserContext browserContext, ViewGroup containerView, InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, - boolean isAccessFromFileURLsGrantedByDefault, AwLayoutSizer layoutSizer) { + AwLayoutSizer layoutSizer) { mBrowserContext = browserContext; mContainerView = containerView; mInternalAccessAdapter = internalAccessAdapter; - mDIPScale = DeviceDisplayInfo.create(containerView.getContext()).getDIPScale(); - // Note that ContentViewCore must be set up before AwContents, as ContentViewCore - // setup performs process initialisation work needed by AwContents. - mContentsClientBridge = new AwContentsClientBridge(contentsClient); + mContentsClient = contentsClient; mLayoutSizer = layoutSizer; + mDIPScale = DeviceDisplayInfo.create(containerView.getContext()).getDIPScale(); mLayoutSizer.setDelegate(new AwLayoutSizerDelegate()); mLayoutSizer.setDIPScale(mDIPScale); mWebContentsDelegate = new AwWebContentsDelegateAdapter(contentsClient, mLayoutSizer.getPreferredSizeChangedListener()); - mNativeAwContents = nativeInit(mWebContentsDelegate, mContentsClientBridge); - mContentsClient = contentsClient; + mContentsClientBridge = new AwContentsClientBridge(contentsClient); + mZoomControls = new AwZoomControls(this); + mIoThreadClient = new IoThreadClientImpl(); + mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(); + + boolean hasInternetPermission = containerView.getContext().checkPermission( + android.Manifest.permission.INTERNET, + Process.myPid(), + Process.myUid()) == PackageManager.PERMISSION_GRANTED; + AwSettings.ZoomSupportChangeListener zoomListener = + new AwSettings.ZoomSupportChangeListener() { + @Override + public void onMultiTouchZoomSupportChanged(boolean supportsMultiTouchZoom) { + mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom); + } + }; + mSettings = new AwSettings(hasInternetPermission, zoomListener, + mBrowserContext.mIsFileAccessGrantedByDefault, mDIPScale); + mDefaultVideoPosterRequestHandler = new DefaultVideoPosterRequestHandler(mContentsClient); + mSettings.setDefaultVideoPosterURL( + mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL()); + mContentsClient.setDIPScale(mDIPScale); + + setNewAwContents(nativeInit(browserContext)); + } + + /** + * Common initialization routine for adopting a native AwContents instance into this + * java instance. + * + * TAKE CARE! This method can get called multiple times per java instance. Code accordingly. + * ^^^^^^^^^ See the native class declaration for more details on relative object lifetimes. + */ + private void setNewAwContents(int newAwContentsPtr) { + if (mNativeAwContents != 0) { + destroy(); + mContentViewCore = null; + } + + assert mNativeAwContents == 0 && mCleanupReference == null && mContentViewCore == null; + + mNativeAwContents = newAwContentsPtr; + // TODO(joth): when the native and java counterparts of AwBrowserContext are hooked up to + // each other, we should update |mBrowserContext| according to the newly received native + // WebContent's browser context. + + // The native side object has been bound to this java instance, so now is the time to + // bind all the native->java relationships. mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents)); int nativeWebContents = nativeGetWebContents(mNativeAwContents); - mZoomControls = new AwZoomControls(this); mContentViewCore = createAndInitializeContentViewCore( - containerView, internalAccessAdapter, nativeWebContents, + mContainerView, mInternalAccessAdapter, nativeWebContents, new AwPinchGestureStateListener(), mContentsClient.getContentViewClient(), mZoomControls); + nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge, + mIoThreadClient, mInterceptNavigationDelegate); mContentsClient.installWebContentsObserver(mContentViewCore); + mSettings.setWebContents(nativeWebContents); + } - mSettings = new AwSettings(mContentViewCore.getContext(), nativeWebContents, - mContentViewCore, isAccessFromFileURLsGrantedByDefault); - setIoThreadClient(new IoThreadClientImpl()); - setInterceptNavigationDelegate(new InterceptNavigationDelegateImpl()); - - mPossiblyStaleHitTestData = new HitTestData(); - nativeDidInitializeContentViewCore(mNativeAwContents, - mContentViewCore.getNativeContentViewCore()); - - mContentsClient.setDIPScale(mDIPScale); - mSettings.setDIPScale(mDIPScale); - mDefaultVideoPosterRequestHandler = new DefaultVideoPosterRequestHandler(mContentsClient); - mSettings.setDefaultVideoPosterURL( - mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL()); - } - - // Only valid within updatePhysicalBackingSizeIfNeeded(). - private final Rect mGlobalVisibleBoundsTemporary = new Rect(); - - private void updatePhysicalBackingSizeIfNeeded() { - // We musn't let the physical backing size get too big, otherwise we - // will try to allocate a SurfaceTexture beyond what the GL driver can - // cope with. In most cases, limiting the SurfaceTexture size to that - // of the visible bounds of the WebView will be good enough i.e. the maximum - // SurfaceTexture dimensions will match the screen dimensions). - mContainerView.getGlobalVisibleRect(mGlobalVisibleBoundsTemporary); - int width = mGlobalVisibleBoundsTemporary.width(); - int height = mGlobalVisibleBoundsTemporary.height(); - if (width != mLastGlobalVisibleWidth || height != mLastGlobalVisibleHeight) { - mLastGlobalVisibleWidth = width; - mLastGlobalVisibleHeight = height; - mContentViewCore.onPhysicalBackingSizeChanged(width, height); + /** + * Called on the "source" AwContents that is opening the popup window to + * provide the AwContents to host the pop up content. + */ + public void supplyContentsForPopup(AwContents newContents) { + int popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents); + if (popupNativeAwContents == 0) { + Log.w(TAG, "Popup WebView bind failed: no pending content."); + if (newContents != null) newContents.destroy(); + return; + } + if (newContents == null) { + nativeDestroy(popupNativeAwContents); + return; } - } - - @VisibleForTesting - public ContentViewCore getContentViewCore() { - return mContentViewCore; - } - // Can be called from any thread. - public AwSettings getSettings() { - return mSettings; + newContents.receivePopupContents(popupNativeAwContents); } - public void setIoThreadClient(AwContentsIoThreadClient ioThreadClient) { - mIoThreadClient = ioThreadClient; - nativeSetIoThreadClient(mNativeAwContents, mIoThreadClient); - } + // Recap: supplyContentsForPopup() is called on the parent window's content, this method is + // called on the popup window's content. + private void receivePopupContents(int popupNativeAwContents) { + setNewAwContents(popupNativeAwContents); - private void setInterceptNavigationDelegate(InterceptNavigationDelegateImpl delegate) { - mInterceptNavigationDelegate = delegate; - nativeSetInterceptNavigationDelegate(mNativeAwContents, delegate); + // Finally refresh view size and visibility, to poke new values into ContentViewCore. + mLastGlobalVisibleWidth = 0; + mLastGlobalVisibleHeight = 0; + onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); + updateVisiblityState(); + onWindowFocusChanged(mWindowFocused); } public void destroy() { mContentViewCore.destroy(); - // The native part of AwSettings isn't needed for the IoThreadClient instance. - mSettings.destroy(); // We explicitly do not null out the mContentViewCore reference here // because ContentViewCore already has code to deal with the case // methods are called on it after it's been destroyed, and other // code relies on AwContents.mContentViewCore to be non-null. + mCleanupReference.cleanupNow(); mNativeAwContents = 0; + mCleanupReference = null; + } + + @VisibleForTesting + public ContentViewCore getContentViewCore() { + return mContentViewCore; + } + + // Can be called from any thread. + public AwSettings getSettings() { + return mSettings; } public static void setAwDrawSWFunctionTable(int functionTablePointer) { @@ -458,6 +504,29 @@ public class AwContents { return nativeGetAwDrawGLViewContext(mNativeAwContents); } + // Only valid within updatePhysicalBackingSizeIfNeeded(). + private final Rect mGlobalVisibleBoundsTemporary = new Rect(); + + private void updatePhysicalBackingSizeIfNeeded() { + // We musn't let the physical backing size get too big, otherwise we + // will try to allocate a SurfaceTexture beyond what the GL driver can + // cope with. In most cases, limiting the SurfaceTexture size to that + // of the visible bounds of the WebView will be good enough i.e. the maximum + // SurfaceTexture dimensions will match the screen dimensions). + mContainerView.getGlobalVisibleRect(mGlobalVisibleBoundsTemporary); + int width = mGlobalVisibleBoundsTemporary.width(); + int height = mGlobalVisibleBoundsTemporary.height(); + if (width != mLastGlobalVisibleWidth || height != mLastGlobalVisibleHeight) { + mLastGlobalVisibleWidth = width; + mLastGlobalVisibleHeight = height; + mContentViewCore.onPhysicalBackingSizeChanged(width, height); + } + } + + //-------------------------------------------------------------------------------------------- + // WebView[Provider] method implementations (where not provided by ContentViewCore) + //-------------------------------------------------------------------------------------------- + // Only valid within onDraw(). private final Rect mClipBoundsTemporary = new Rect(); @@ -602,50 +671,6 @@ public class AwContents { if (url == null || url.trim().isEmpty()) return null; return url; } - /** - * Called on the "source" AwContents that is opening the popup window to - * provide the AwContents to host the pop up content. - */ - public void supplyContentsForPopup(AwContents newContents) { - int popupWebContents = nativeReleasePopupWebContents(mNativeAwContents); - assert popupWebContents != 0; - newContents.setNewWebContents(popupWebContents); - } - - private void setNewWebContents(int newWebContentsPtr) { - // When setting a new WebContents, we new up a ContentViewCore that will - // wrap it and then swap it. - ContentViewCore newCore = createAndInitializeContentViewCore( - mContainerView, mInternalAccessAdapter, newWebContentsPtr, - new AwPinchGestureStateListener(), mContentsClient.getContentViewClient(), - mZoomControls); - mContentsClient.installWebContentsObserver(newCore); - - // Now swap the Java side reference. - mContentViewCore.destroy(); - mContentViewCore = newCore; - - // Now rewire native side to use the new WebContents. - nativeSetWebContents(mNativeAwContents, newWebContentsPtr); - nativeSetIoThreadClient(mNativeAwContents, mIoThreadClient); - nativeSetInterceptNavigationDelegate(mNativeAwContents, mInterceptNavigationDelegate); - - // This will also apply settings to the new WebContents. - mSettings.setWebContents(newWebContentsPtr); - - // Finally poke the new ContentViewCore with the size of the container view and show it. - if (mContainerView.getWidth() != 0 || mContainerView.getHeight() != 0) { - mContentViewCore.onSizeChanged( - mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); - } - nativeDidInitializeContentViewCore(mNativeAwContents, - mContentViewCore.getNativeContentViewCore()); - if (mContainerView.getVisibility() == View.VISIBLE) { - // The popup window was hidden when we prompted the embedder to display - // it, so show it again now we have a container. - mContentViewCore.onShow(); - } - } public void requestFocus() { if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) { @@ -661,10 +686,6 @@ public class AwContents { return mZoomControls.getZoomControlsViewForTest(); } - //-------------------------------------------------------------------------------------------- - // WebView[Provider] method implementations (where not provided by ContentViewCore) - //-------------------------------------------------------------------------------------------- - /** * @see ContentViewCore#getContentSettings() */ @@ -1425,26 +1446,22 @@ public class AwContents { // Native methods //-------------------------------------------------------------------------------------------- - private native int nativeInit(AwWebContentsDelegate webViewWebContentsDelegate, - AwContentsClientBridge contentsClientBridge); + private static native int nativeInit(AwBrowserContext browserContext); private static native void nativeDestroy(int nativeAwContents); private static native void nativeSetAwDrawSWFunctionTable(int functionTablePointer); private static native void nativeSetAwDrawGLFunctionTable(int functionTablePointer); private static native int nativeGetAwDrawGLFunction(); - + private native void nativeSetJavaPeers(int nativeAwContents, AwContents awContents, + AwWebContentsDelegate webViewWebContentsDelegate, + AwContentsClientBridge contentsClientBridge, + AwContentsIoThreadClient ioThreadClient, + InterceptNavigationDelegate navigationInterceptionDelegate); private native int nativeGetWebContents(int nativeAwContents); - private native void nativeDidInitializeContentViewCore(int nativeAwContents, - int nativeContentViewCore); private native void nativeDocumentHasImages(int nativeAwContents, Message message); private native void nativeGenerateMHTML( int nativeAwContents, String path, ValueCallback<String> callback); - private native void nativeSetIoThreadClient(int nativeAwContents, - AwContentsIoThreadClient ioThreadClient); - private native void nativeSetInterceptNavigationDelegate(int nativeAwContents, - InterceptNavigationDelegate navigationInterceptionDelegate); - private native void nativeAddVisitedLinks(int nativeAwContents, String[] visitedLinks); private native boolean nativeOnDraw(int nativeAwContents, Canvas canvas, boolean isHardwareAccelerated, int scrollX, int ScrollY, @@ -1471,8 +1488,7 @@ public class AwContents { // Returns false if restore state fails. private native boolean nativeRestoreFromOpaqueState(int nativeAwContents, byte[] state); - private native int nativeReleasePopupWebContents(int nativeAwContents); - private native void nativeSetWebContents(int nativeAwContents, int nativeNewWebContents); + private native int nativeReleasePopupAwContents(int nativeAwContents); private native void nativeFocusFirstNode(int nativeAwContents); private native int nativeGetAwDrawGLViewContext(int nativeAwContents); diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java index 2be4d46..0f3ddc2 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java +++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java @@ -4,12 +4,9 @@ package org.chromium.android_webview; -import android.content.Context; -import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.Process; import android.webkit.WebSettings.PluginState; import android.webkit.WebSettings; import android.webkit.WebView; @@ -43,12 +40,14 @@ public class AwSettings { // used from any thread. Internally, the class uses a message queue // to call native code on the UI thread only. + // Values passed in on construction. + private final boolean mHasInternetPermission; + private final ZoomSupportChangeListener mZoomChangeListener; + private final double mDIPScale; + // Lock to protect all settings. private final Object mAwSettingsLock = new Object(); - private final Context mContext; - private double mDIPScale; - private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS; private int mTextSizePercent = 100; private String mStandardFontFamily = "sans-serif"; @@ -94,6 +93,7 @@ public class AwSettings { private boolean mSupportZoom = true; private boolean mBuiltInZoomControls = false; private boolean mDisplayZoomControls = true; + static class LazyDefaultUserAgent{ // Lazy Holder pattern private static final String sInstance = nativeGetDefaultUserAgent(); @@ -106,11 +106,9 @@ public class AwSettings { // client. private static boolean sAppCachePathIsSet = false; - // The native side of this object. + // The native side of this object. It's lifetime is bounded by the WebContent it is attached to. private int mNativeAwSettings = 0; - private ContentViewCore mContentViewCore; - // A flag to avoid sending superfluous synchronization messages. private boolean mIsUpdateWebkitPrefsMessagePending = false; // Custom handler that queues messages to call native code on the UI thread. @@ -166,44 +164,39 @@ public class AwSettings { } } - public AwSettings(Context context, - int nativeWebContents, - ContentViewCore contentViewCore, - boolean isAccessFromFileURLsGrantedByDefault) { - ThreadUtils.assertOnUiThread(); - mContext = context; - mBlockNetworkLoads = mContext.checkPermission( - android.Manifest.permission.INTERNET, - Process.myPid(), - Process.myUid()) != PackageManager.PERMISSION_GRANTED; - mContentViewCore = contentViewCore; - mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoomLocked()); + interface ZoomSupportChangeListener { + public void onMultiTouchZoomSupportChanged(boolean supportsMultiTouchZoom); + } - if (isAccessFromFileURLsGrantedByDefault) { - mAllowUniversalAccessFromFileURLs = true; - mAllowFileAccessFromFileURLs = true; - } + public AwSettings(boolean hasInternetPermission, + ZoomSupportChangeListener zoomChangeListener, + boolean isAccessFromFileURLsGrantedByDefault, + double dipScale) { + ThreadUtils.assertOnUiThread(); + synchronized (mAwSettingsLock) { + mHasInternetPermission = hasInternetPermission; + mZoomChangeListener = zoomChangeListener; + mDIPScale = dipScale; + mEventHandler = new EventHandler(); + mBlockNetworkLoads = !hasInternetPermission; - mEventHandler = new EventHandler(); - mUserAgent = LazyDefaultUserAgent.sInstance; + if (isAccessFromFileURLsGrantedByDefault) { + mAllowUniversalAccessFromFileURLs = true; + mAllowFileAccessFromFileURLs = true; + } - synchronized (mAwSettingsLock) { - mNativeAwSettings = nativeInit(nativeWebContents); + mUserAgent = LazyDefaultUserAgent.sInstance; + onMultiTouchZoomSupportChanged(supportsMultiTouchZoomLocked()); } - assert mNativeAwSettings != 0; + // Defer initializing the native side until a native WebContents instance is set. } - public void destroy() { - nativeDestroy(mNativeAwSettings); + @CalledByNative + private void nativeAwSettingsGone(int nativeAwSettings) { + assert mNativeAwSettings != 0 && mNativeAwSettings == nativeAwSettings; mNativeAwSettings = 0; } - public void setDIPScale(double dipScale) { - synchronized (mAwSettingsLock) { - mDIPScale = dipScale; - } - } - @CalledByNative private double getDIPScaleLocked() { return mDIPScale; @@ -211,7 +204,14 @@ public class AwSettings { public void setWebContents(int nativeWebContents) { synchronized (mAwSettingsLock) { - nativeSetWebContentsLocked(mNativeAwSettings, nativeWebContents); + if (mNativeAwSettings != 0) { + nativeDestroy(mNativeAwSettings); + assert mNativeAwSettings == 0; // nativeAwSettingsGone should have been called. + } + if (nativeWebContents != 0) { + mNativeAwSettings = nativeInit(nativeWebContents); + nativeUpdateEverythingLocked(mNativeAwSettings); + } } } @@ -220,10 +220,7 @@ public class AwSettings { */ public void setBlockNetworkLoads(boolean flag) { synchronized (mAwSettingsLock) { - if (!flag && mContext.checkPermission( - android.Manifest.permission.INTERNET, - Process.myPid(), - Process.myUid()) != PackageManager.PERMISSION_GRANTED) { + if (!flag && !mHasInternetPermission) { throw new SecurityException("Permission denied - " + "application missing INTERNET permission"); } @@ -1239,11 +1236,12 @@ public class AwSettings { return mDefaultVideoPosterURL; } - private void updateMultiTouchZoomSupport(final boolean supportsMultiTouchZoom) { - ThreadUtils.runOnUiThreadBlocking(new Runnable() { + private void onMultiTouchZoomSupportChanged(final boolean supportsMultiTouchZoom) { + // Always post asynchronously here, to avoid doubling back onto the caller. + ThreadUtils.postOnUiThread(new Runnable() { @Override public void run() { - mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom); + mZoomChangeListener.onMultiTouchZoomSupportChanged(supportsMultiTouchZoom); } }); } @@ -1255,7 +1253,7 @@ public class AwSettings { synchronized (mAwSettingsLock) { if (mSupportZoom != support) { mSupportZoom = support; - updateMultiTouchZoomSupport(supportsMultiTouchZoomLocked()); + onMultiTouchZoomSupportChanged(supportsMultiTouchZoomLocked()); } } } @@ -1276,7 +1274,7 @@ public class AwSettings { synchronized (mAwSettingsLock) { if (mBuiltInZoomControls != enabled) { mBuiltInZoomControls = enabled; - updateMultiTouchZoomSupport(supportsMultiTouchZoomLocked()); + onMultiTouchZoomSupportChanged(supportsMultiTouchZoomLocked()); } } } @@ -1309,6 +1307,7 @@ public class AwSettings { } private boolean supportsMultiTouchZoomLocked() { + assert Thread.holdsLock(mAwSettingsLock); return mSupportZoom && mBuiltInZoomControls; } @@ -1353,8 +1352,6 @@ public class AwSettings { private native void nativeResetScrollAndScaleState(int nativeAwSettings); - private native void nativeSetWebContentsLocked(int nativeAwSettings, int nativeWebContents); - private native void nativeUpdateEverythingLocked(int nativeAwSettings); private native void nativeUpdateInitialPageScaleLocked(int nativeAwSettings); diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java index 69a2c50a..3bdeb31 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java @@ -217,14 +217,17 @@ public class AwTestBase return testContainerView; } + // The browser context needs to be a process-wide singleton. + private AwBrowserContext mBrowserContext = + new AwBrowserContext(new InMemorySharedPreferences(), false); + protected AwTestContainerView createDetachedAwTestContainerView( final AwContentsClient awContentsClient, final TestDependencyFactory testDependencyFactory) { final AwTestContainerView testContainerView = new AwTestContainerView(getActivity()); testContainerView.initialize(new AwContents( - new AwBrowserContext(new InMemorySharedPreferences()), - testContainerView, testContainerView.getInternalAccessDelegate(), - awContentsClient, false, testDependencyFactory.createLayoutSizer())); + mBrowserContext, testContainerView, testContainerView.getInternalAccessDelegate(), + awContentsClient, testDependencyFactory.createLayoutSizer())); return testContainerView; } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java index 1f36e68..76d2265 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java @@ -58,9 +58,9 @@ public class LoadUrlTest extends AwTestBase { @SmallTest @Feature({"AndroidWebView"}) public void testDataUrlCharset() throws Throwable { - // Note that the '£' is the important character in the following + // Note that the \u00a3 (pound sterling) is the important character in the following // string as it's not in the US_ASCII character set. - final String expectedTitle = "You win £100!"; + final String expectedTitle = "You win \u00a3100!"; final String data = "<html><head><title>" + expectedTitle + "</title></head><body>foo</body></html>"; final TestAwContentsClient contentsClient = new TestAwContentsClient(); diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index 8d726da..142f413 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc @@ -79,7 +79,11 @@ namespace android_webview { namespace { -static JavaBrowserViewRendererHelper java_renderer_helper; +JavaBrowserViewRendererHelper* java_renderer_helper() { + static JavaBrowserViewRendererHelper* g_instance + = new JavaBrowserViewRendererHelper; + return g_instance; +} const void* kAwContentsUserDataKey = &kAwContentsUserDataKey; @@ -118,38 +122,15 @@ AwContents* AwContents::FromID(int render_process_id, int render_view_id) { return FromWebContents(web_contents); } -AwContents::AwContents(JNIEnv* env, - jobject obj, - jobject web_contents_delegate, - jobject contents_client_bridge) - : java_ref_(env, obj), - web_contents_delegate_( - new AwWebContentsDelegate(env, web_contents_delegate)), - contents_client_bridge_( - new AwContentsClientBridge(env, contents_client_bridge)), +AwContents::AwContents(scoped_ptr<WebContents> web_contents) + : web_contents_(web_contents.Pass()), browser_view_renderer_( - new InProcessViewRenderer(this, &java_renderer_helper)) { - android_webview::AwBrowserDependencyFactory* dependency_factory = - android_webview::AwBrowserDependencyFactory::GetInstance(); - - // TODO(joth): rather than create and set the WebContents here, expose the - // factory method to java side and have that orchestrate the construction - // order. - SetWebContents(dependency_factory->CreateWebContents()); -} - -void AwContents::SetWebContents(content::WebContents* web_contents) { - web_contents_.reset(web_contents); - if (find_helper_.get()) { - find_helper_->SetListener(NULL); - } + new InProcessViewRenderer(this, java_renderer_helper(), + web_contents_.get())) { icon_helper_.reset(new IconHelper(web_contents_.get())); icon_helper_->SetListener(this); web_contents_->SetUserData(kAwContentsUserDataKey, new AwContentsUserData(this)); - AwContentsClientBridgeBase::Associate(web_contents_.get(), - contents_client_bridge_.get()); - web_contents_->SetDelegate(web_contents_delegate_.get()); render_view_host_ext_.reset( new AwRenderViewHostExt(this, web_contents_.get())); @@ -160,6 +141,40 @@ void AwContents::SetWebContents(content::WebContents* web_contents) { InitAutofillIfNecessary(autofill_manager_delegate->GetSaveFormData()); } +void AwContents::SetJavaPeers(JNIEnv* env, + jobject obj, + jobject aw_contents, + jobject web_contents_delegate, + jobject contents_client_bridge, + jobject io_thread_client, + jobject intercept_navigation_delegate) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // The |aw_content| param is technically spurious as it duplicates |obj| but + // is passed over anyway to make the binding more explicit. + java_ref_ = JavaObjectWeakGlobalRef(env, aw_contents); + + web_contents_delegate_.reset( + new AwWebContentsDelegate(env, web_contents_delegate)); + web_contents_->SetDelegate(web_contents_delegate_.get()); + + contents_client_bridge_.reset( + new AwContentsClientBridge(env, contents_client_bridge)); + AwContentsClientBridgeBase::Associate(web_contents_.get(), + contents_client_bridge_.get()); + + AwContentsIoThreadClientImpl::Associate( + web_contents_.get(), ScopedJavaLocalRef<jobject>(env, io_thread_client)); + int child_id = web_contents_->GetRenderProcessHost()->GetID(); + int route_id = web_contents_->GetRoutingID(); + AwResourceDispatcherHostDelegate::OnIoThreadClientReady(child_id, route_id); + + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + InterceptNavigationDelegate::Associate( + web_contents_.get(), + make_scoped_ptr(new InterceptNavigationDelegate( + env, intercept_navigation_delegate))); +} + void AwContents::SetSaveFormData(bool enabled) { InitAutofillIfNecessary(enabled); // We need to check for the existence, since autofill_manager_delegate @@ -196,10 +211,6 @@ void AwContents::InitAutofillIfNecessary(bool enabled) { AutofillExternalDelegate::FromWebContents(web_contents)); } -void AwContents::SetWebContents(JNIEnv* env, jobject obj, jint new_wc) { - SetWebContents(reinterpret_cast<content::WebContents*>(new_wc)); -} - AwContents::~AwContents() { DCHECK(AwContents::FromWebContents(web_contents_.get()) == this); web_contents_->RemoveUserData(kAwContentsUserDataKey); @@ -210,34 +221,35 @@ AwContents::~AwContents() { } jint AwContents::GetWebContents(JNIEnv* env, jobject obj) { + DCHECK(web_contents_); return reinterpret_cast<jint>(web_contents_.get()); } -void AwContents::DidInitializeContentViewCore(JNIEnv* env, jobject obj, - jint content_view_core) { - ContentViewCore* core = reinterpret_cast<ContentViewCore*>(content_view_core); - DCHECK(core == ContentViewCore::FromWebContents(web_contents_.get())); - browser_view_renderer_->SetContents(core); -} - void AwContents::Destroy(JNIEnv* env, jobject obj) { delete this; } -// static -void SetAwDrawSWFunctionTable(JNIEnv* env, jclass, jint function_table) { +static jint Init(JNIEnv* env, jclass, jobject browser_context) { + // TODO(joth): Use |browser_context| to get the native BrowserContext, rather + // than hard-code the default instance lookup here. + scoped_ptr<WebContents> web_contents(content::WebContents::Create( + content::WebContents::CreateParams(AwBrowserContext::GetDefault()))); + // Return an 'uninitialized' instance; most work is deferred until the + // subsequent SetJavaPeers() call. + return reinterpret_cast<jint>(new AwContents(web_contents.Pass())); +} + +static void SetAwDrawSWFunctionTable(JNIEnv* env, jclass, jint function_table) { BrowserViewRenderer::SetAwDrawSWFunctionTable( reinterpret_cast<AwDrawSWFunctionTable*>(function_table)); } -// static -void SetAwDrawGLFunctionTable(JNIEnv* env, jclass, jint function_table) { +static void SetAwDrawGLFunctionTable(JNIEnv* env, jclass, jint function_table) { GpuMemoryBufferImpl::SetAwDrawGLFunctionTable( reinterpret_cast<AwDrawGLFunctionTable*>(function_table)); } -// static -jint GetAwDrawGLFunction(JNIEnv* env, jclass) { +static jint GetAwDrawGLFunction(JNIEnv* env, jclass) { return reinterpret_cast<jint>(&DrawGLFunction); } @@ -306,24 +318,6 @@ bool AwContents::OnReceivedHttpAuthRequest(const JavaRef<jobject>& handler, return true; } -void AwContents::SetIoThreadClient(JNIEnv* env, jobject obj, jobject client) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - AwContentsIoThreadClientImpl::Associate( - web_contents_.get(), ScopedJavaLocalRef<jobject>(env, client)); - int child_id = web_contents_->GetRenderProcessHost()->GetID(); - int route_id = web_contents_->GetRoutingID(); - AwResourceDispatcherHostDelegate::OnIoThreadClientReady(child_id, route_id); -} - -void AwContents::SetInterceptNavigationDelegate(JNIEnv* env, - jobject obj, - jobject delegate) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - InterceptNavigationDelegate::Associate( - web_contents_.get(), - make_scoped_ptr(new InterceptNavigationDelegate(env, delegate))); -} - void AwContents::AddVisitedLinks(JNIEnv* env, jobject obj, jobjectArray jvisited_links) { @@ -343,15 +337,6 @@ void AwContents::AddVisitedLinks(JNIEnv* env, ->AddVisitedURLs(visited_link_gurls); } -static jint Init(JNIEnv* env, - jobject obj, - jobject web_contents_delegate, - jobject contents_client_bridge) { - AwContents* tab = new AwContents(env, obj, web_contents_delegate, - contents_client_bridge); - return reinterpret_cast<jint>(tab); -} - bool RegisterAwContents(JNIEnv* env) { return RegisterNativesImpl(env) >= 0; } @@ -673,14 +658,14 @@ void AwContents::SetPendingWebContentsForPopup( base::MessageLoop::current()->DeleteSoon(FROM_HERE, pending.release()); return; } - pending_contents_ = pending.Pass(); + pending_contents_.reset(new AwContents(pending.Pass())); } void AwContents::FocusFirstNode(JNIEnv* env, jobject obj) { web_contents_->FocusThroughTabTraversal(false); } -jint AwContents::ReleasePopupWebContents(JNIEnv* env, jobject obj) { +jint AwContents::ReleasePopupAwContents(JNIEnv* env, jobject obj) { return reinterpret_cast<jint>(pending_contents_.release()); } diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h index 170e9b7..1189e9f 100644 --- a/android_webview/native/aw_contents.h +++ b/android_webview/native/aw_contents.h @@ -36,6 +36,15 @@ class AwWebContentsDelegate; // Provides the ownership of and access to browser components required for // WebView functionality; analogous to chrome's TabContents, but with a // level of indirection provided by the AwContentsContainer abstraction. +// +// Object lifetime: +// For most purposes the java and native objects can be considered to have +// 1:1 lifetime and relationship. The exception is the java instance that +// hosts a popup will be rebound to a second native instance (carrying the +// popup content) and discard the 'default' native instance it made on +// construction. A native instance is only bound to at most one Java peer over +// its entire lifetime - see Init() and SetPendingWebContentsForPopup() for the +// construction points, and SetJavaPeers() where these paths join. class AwContents : public FindHelper::Listener, public IconHelper::Listener, public AwRenderViewHostExtClient, @@ -48,10 +57,7 @@ class AwContents : public FindHelper::Listener, // render_process_id and render_view_id, or NULL. static AwContents* FromID(int render_process_id, int render_view_id); - AwContents(JNIEnv* env, - jobject obj, - jobject web_contents_delegate, - jobject contents_client_bridge); + AwContents(scoped_ptr<content::WebContents> web_contents); virtual ~AwContents(); AwRenderViewHostExt* render_view_host_ext() { @@ -67,18 +73,19 @@ class AwContents : public FindHelper::Listener, const std::string& realm); // Methods called from Java. + void SetJavaPeers(JNIEnv* env, + jobject obj, + jobject aw_contents, + jobject web_contents_delegate, + jobject contents_client_bridge, + jobject io_thread_client, + jobject intercept_navigation_delegate); jint GetWebContents(JNIEnv* env, jobject obj); - void SetWebContents(JNIEnv* env, jobject obj, jint web_contents); jint GetAwContentsClientBridge(JNIEnv* env, jobject obj); - void DidInitializeContentViewCore(JNIEnv* env, jobject obj, - jint content_view_core); void Destroy(JNIEnv* env, jobject obj); void DocumentHasImages(JNIEnv* env, jobject obj, jobject message); void GenerateMHTML(JNIEnv* env, jobject obj, jstring jpath, jobject callback); - void SetIoThreadClient(JNIEnv* env, jobject obj, jobject client); - void SetInterceptNavigationDelegate(JNIEnv* env, jobject obj, - jobject delegate); void AddVisitedLinks(JNIEnv* env, jobject obj, jobjectArray jvisited_links); base::android::ScopedJavaLocalRef<jbyteArray> GetCertificate( JNIEnv* env, jobject obj); @@ -145,12 +152,11 @@ class AwContents : public FindHelper::Listener, void ClearCache(JNIEnv* env, jobject obj, jboolean include_disk_files); void SetPendingWebContentsForPopup(scoped_ptr<content::WebContents> pending); - jint ReleasePopupWebContents(JNIEnv* env, jobject obj); + jint ReleasePopupAwContents(JNIEnv* env, jobject obj); void SetSaveFormData(bool enabled); private: - void SetWebContents(content::WebContents* web_contents); void InitAutofillIfNecessary(bool enabled); JavaObjectWeakGlobalRef java_ref_; @@ -160,7 +166,7 @@ class AwContents : public FindHelper::Listener, scoped_ptr<AwRenderViewHostExt> render_view_host_ext_; scoped_ptr<FindHelper> find_helper_; scoped_ptr<IconHelper> icon_helper_; - scoped_ptr<content::WebContents> pending_contents_; + scoped_ptr<AwContents> pending_contents_; scoped_ptr<BrowserViewRenderer> browser_view_renderer_; // GURL is supplied by the content layer as requesting frame. diff --git a/android_webview/native/aw_settings.cc b/android_webview/native/aw_settings.cc index 8d44487..15c60aa 100644 --- a/android_webview/native/aw_settings.cc +++ b/android_webview/native/aw_settings.cc @@ -24,11 +24,19 @@ using base::android::ScopedJavaLocalRef; namespace android_webview { -AwSettings::AwSettings(JNIEnv* env, jobject obj) - : aw_settings_(env, obj) { +AwSettings::AwSettings(JNIEnv* env, jobject obj, jint web_contents) + : WebContentsObserver( + reinterpret_cast<content::WebContents*>(web_contents)), + aw_settings_(env, obj) { } AwSettings::~AwSettings() { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env); + jobject obj = scoped_obj.obj(); + if (!obj) return; + Java_AwSettings_nativeAwSettingsGone(env, obj, + reinterpret_cast<jint>(this)); } void AwSettings::Destroy(JNIEnv* env, jobject obj) { @@ -48,15 +56,6 @@ void AwSettings::ResetScrollAndScaleState(JNIEnv* env, jobject obj) { rvhe->ResetScrollAndScaleState(); } -void AwSettings::SetWebContentsLocked( - JNIEnv* env, jobject obj, jint jweb_contents) { - content::WebContents* web_contents = - reinterpret_cast<content::WebContents*>(jweb_contents); - Observe(web_contents); - - UpdateEverythingLocked(env, obj); -} - void AwSettings::UpdateEverything() { JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); @@ -250,12 +249,14 @@ void AwSettings::RenderViewCreated(content::RenderViewHost* render_view_host) { UpdateEverything(); } -// Assumed to be called from the Java object's constructor, thus is "Locked". +void AwSettings::WebContentsDestroyed(content::WebContents* web_contents) { + delete this; +} + static jint Init(JNIEnv* env, jobject obj, jint web_contents) { - AwSettings* settings = new AwSettings(env, obj); - settings->SetWebContentsLocked(env, obj, web_contents); + AwSettings* settings = new AwSettings(env, obj, web_contents); return reinterpret_cast<jint>(settings); } diff --git a/android_webview/native/aw_settings.h b/android_webview/native/aw_settings.h index 4d0d5a0..d547f34 100644 --- a/android_webview/native/aw_settings.h +++ b/android_webview/native/aw_settings.h @@ -18,14 +18,13 @@ class AwRenderViewHostExt; class AwSettings : public content::WebContentsObserver { public: - AwSettings(JNIEnv* env, jobject obj); + AwSettings(JNIEnv* env, jobject obj, jint web_contents); virtual ~AwSettings(); // Called from Java. Methods with "Locked" suffix require that the settings // access lock is held during their execution. void Destroy(JNIEnv* env, jobject obj); void ResetScrollAndScaleState(JNIEnv* env, jobject obj); - void SetWebContentsLocked(JNIEnv* env, jobject obj, jint web_contents); void UpdateEverythingLocked(JNIEnv* env, jobject obj); void UpdateInitialPageScaleLocked(JNIEnv* env, jobject obj); void UpdateUserAgentLocked(JNIEnv* env, jobject obj); @@ -40,6 +39,8 @@ class AwSettings : public content::WebContentsObserver { // WebContentsObserver overrides: virtual void RenderViewCreated( content::RenderViewHost* render_view_host) OVERRIDE; + virtual void WebContentsDestroyed( + content::WebContents* web_contents) OVERRIDE; JavaObjectWeakGlobalRef aw_settings_; }; diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java index e2d6dcf..5646bc1 100644 --- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java +++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java @@ -39,6 +39,7 @@ import org.chromium.content.browser.LoadUrlParams; public class AwShellActivity extends Activity { private final static String PREFERENCES_NAME = "AwShellPrefs"; private final static String INITIAL_URL = "about:blank"; + private AwBrowserContext mBrowserContext; private AwTestContainerView mAwTestContainerView; private EditText mUrlTextView; private ImageButton mPrevButton; @@ -83,11 +84,12 @@ public class AwShellActivity extends Activity { SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); - AwBrowserContext browserContext = new AwBrowserContext(sharedPreferences); - - testContainerView.initialize(new AwContents(browserContext, testContainerView, + if (mBrowserContext == null) { + mBrowserContext = new AwBrowserContext(sharedPreferences, false); + } + testContainerView.initialize(new AwContents(mBrowserContext, testContainerView, testContainerView.getInternalAccessDelegate(), - awContentsClient, false)); + awContentsClient)); testContainerView.getAwContents().getSettings().setJavaScriptEnabled(true); return testContainerView; } diff --git a/content/browser/android/in_process/synchronous_input_event_filter.cc b/content/browser/android/in_process/synchronous_input_event_filter.cc index 30f96a2..57ee7d9 100644 --- a/content/browser/android/in_process/synchronous_input_event_filter.cc +++ b/content/browser/android/in_process/synchronous_input_event_filter.cc @@ -47,8 +47,12 @@ void SynchronousInputEventFilter::SetBoundHandlerOnUIThread( void SynchronousInputEventFilter::DidAddInputHandler( int routing_id, cc::InputHandler* input_handler) { - SynchronousCompositorImpl::FromRoutingID(routing_id) - ->SetInputHandler(input_handler); + // The SynchronusCompositorImpl can be NULL if the WebContents that it's + // bound to has already been deleted. + SynchronousCompositorImpl* compositor = + SynchronousCompositorImpl::FromRoutingID(routing_id); + if (compositor) + compositor->SetInputHandler(input_handler); } void SynchronousInputEventFilter::DidRemoveInputHandler(int routing_id) { |