Bug 1458327 - 1. Pass init-data instead of settings to Window; r?esawin
Instead of passing a live settings object to the native Window, pass a
static initialization data bundle to the Window. The bundle contains
settings at the time of creation. All changes to settings after creation
are updated through events, rather than the live object.
Using a live object between Gecko and UI threads has some drawbacks,
including the need to lock the object, and the fact it won't work with
remote runtimes across processes.
MozReview-Commit-ID: 1DngLfJ0Fnc
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -498,17 +498,17 @@ public class GeckoSession extends LayerS
}
return mBinder;
}
// Create a new Gecko window and assign an initial set of Java session objects to it.
@WrapForJNI(dispatchTo = "proxy")
public static native void open(Window instance, NativeQueue queue,
Compositor compositor, EventDispatcher dispatcher,
- GeckoBundle settings, String id, String chromeUri,
+ GeckoBundle initData, String id, String chromeUri,
int screenId, boolean privateMode);
@Override // JNIObject
public void disposeNative() {
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
nativeDisposeNative();
} else {
GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
@@ -546,44 +546,44 @@ public class GeckoSession extends LayerS
@WrapForJNI(dispatchTo = "proxy", stubName = "Close")
private native void nativeClose();
// Assign a new set of Java session objects to the underlying Gecko window.
// This replaces previously assigned objects from open() or transfer() calls.
public synchronized void transfer(final NativeQueue queue,
final Compositor compositor,
final EventDispatcher dispatcher,
- final GeckoBundle settings) {
+ final GeckoBundle initData) {
if (mNativeQueue == null) {
// Already closed.
return;
}
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- nativeTransfer(queue, compositor, dispatcher, settings);
+ nativeTransfer(queue, compositor, dispatcher, initData);
} else {
GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
this, "nativeTransfer",
NativeQueue.class, queue,
Compositor.class, compositor,
EventDispatcher.class, dispatcher,
- GeckoBundle.class, settings);
+ GeckoBundle.class, initData);
}
if (mNativeQueue != queue) {
// Reset the old queue to prevent old events from affecting this window.
// Gecko will call onReady later with the new queue if needed.
mNativeQueue.reset(State.INITIAL);
mNativeQueue = queue;
}
}
@WrapForJNI(dispatchTo = "proxy", stubName = "Transfer")
private native void nativeTransfer(NativeQueue queue, Compositor compositor,
- EventDispatcher dispatcher, GeckoBundle settings);
+ EventDispatcher dispatcher, GeckoBundle initData);
@WrapForJNI(dispatchTo = "proxy")
public native void attachEditable(IGeckoEditableParent parent,
GeckoEditableChild child);
@WrapForJNI(calledFrom = "gecko")
private synchronized void onReady(final @Nullable NativeQueue queue) {
// onReady is called the first time the Gecko window is ready, with a null queue
@@ -662,17 +662,17 @@ public class GeckoSession extends LayerS
}
mWindow = window;
mSettings = new GeckoSessionSettings(settings, this);
mId = id;
if (mWindow != null) {
mWindow.transfer(mNativeQueue, mCompositor,
- mEventDispatcher, mSettings.asBundle());
+ mEventDispatcher, createInitData());
onWindowChanged(WINDOW_TRANSFER_IN, /* inProgress */ false);
}
}
/* package */ void transferFrom(final GeckoSession session) {
final boolean changing = (session.mWindow != null);
if (changing) {
@@ -728,16 +728,22 @@ public class GeckoSession extends LayerS
public boolean isOpen() {
return mWindow != null;
}
/* package */ boolean isReady() {
return mNativeQueue.isReady();
}
+ private GeckoBundle createInitData() {
+ final GeckoBundle initData = new GeckoBundle(1);
+ initData.putBundle("settings", mSettings.toBundle());
+ return initData;
+ }
+
/**
* Opens the session.
*
* The session is in a 'closed' state when first created. Opening it creates
* the underlying Gecko objects necessary to load a page, etc. Most GeckoSession
* methods only take affect on an open session, and are queued until the session
* is opened here. Opening a session is an asynchronous operation. You can check
* the current state via isOpen().
@@ -762,26 +768,26 @@ public class GeckoSession extends LayerS
final boolean isPrivate = mSettings.getBoolean(GeckoSessionSettings.USE_PRIVATE_MODE);
mWindow = new Window(runtime, mNativeQueue);
onWindowChanged(WINDOW_OPEN, /* inProgress */ true);
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
Window.open(mWindow, mNativeQueue, mCompositor, mEventDispatcher,
- mSettings.asBundle(), mId, chromeUri, screenId, isPrivate);
+ createInitData(), mId, chromeUri, screenId, isPrivate);
} else {
GeckoThread.queueNativeCallUntil(
GeckoThread.State.PROFILE_READY,
Window.class, "open",
Window.class, mWindow,
NativeQueue.class, mNativeQueue,
Compositor.class, mCompositor,
EventDispatcher.class, mEventDispatcher,
- GeckoBundle.class, mSettings.asBundle(),
+ GeckoBundle.class, createInitData(),
String.class, mId,
String.class, chromeUri,
screenId, isPrivate);
}
onWindowChanged(WINDOW_OPEN, /* inProgress */ false);
}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
@@ -160,18 +160,18 @@ public final class GeckoSessionSettings
}
public String getString(final Key<String> key) {
synchronized (mBundle) {
return mBundle.getString(key.name);
}
}
- /* package */ GeckoBundle asBundle() {
- return mBundle;
+ /* package */ GeckoBundle toBundle() {
+ return new GeckoBundle(mBundle);
}
@Override
public String toString() {
return mBundle.toString();
}
@Override
--- a/widget/android/EventDispatcher.cpp
+++ b/widget/android/EventDispatcher.cpp
@@ -820,16 +820,23 @@ EventDispatcher::Dispatch(const char16_t
java::GeckoBundle::Param aData,
nsIAndroidEventCallback* aCallback)
{
nsDependentString event(aEvent);
ListenersList* list = mListenersMap.Get(event);
if (list) {
dom::AutoJSAPI jsapi;
+ if (mDOMWindow) {
+ NS_ENSURE_TRUE(jsapi.Init(mDOMWindow->GetCurrentInnerWindow()),
+ NS_ERROR_FAILURE);
+ } else {
+ NS_ENSURE_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()),
+ NS_ERROR_FAILURE);
+ }
JS::RootedValue data(jsapi.cx());
nsresult rv = UnboxData(/* Event */ nullptr, jsapi.cx(), aData, &data,
/* BundleOnly */ true);
NS_ENSURE_SUCCESS(rv, rv);
return DispatchOnGecko(list, event, data, aCallback);
}
if (!mDispatcher) {
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -66,17 +66,17 @@ interface nsIAndroidEventDispatcher : ns
[implicit_jscontext]
void unregisterListener(in nsIAndroidEventListener listener,
in jsval events);
};
[scriptable, uuid(60a78a94-6117-432f-9d49-304913a931c5)]
interface nsIAndroidView : nsIAndroidEventDispatcher
{
- [implicit_jscontext] readonly attribute jsval settings;
+ [implicit_jscontext] readonly attribute jsval initData;
};
[scriptable, uuid(1beb70d3-70f3-4742-98cc-a3d301b26c0c)]
interface nsIAndroidBridge : nsIAndroidEventDispatcher
{
attribute nsIAndroidBrowserApp browserApp;
void contentDocumentChanged(in mozIDOMWindowProxy window);
boolean isContentDocumentDisplayed(in mozIDOMWindowProxy window);
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -279,31 +279,31 @@ private:
public:
// Create and attach a window.
static void Open(const jni::Class::LocalRef& aCls,
GeckoSession::Window::Param aWindow,
jni::Object::Param aQueue,
jni::Object::Param aCompositor,
jni::Object::Param aDispatcher,
- jni::Object::Param aSettings,
+ jni::Object::Param aInitData,
jni::String::Param aId,
jni::String::Param aChromeURI,
int32_t aScreenId,
bool aPrivateMode);
// Close and destroy the nsWindow.
void Close();
// Transfer this nsWindow to new GeckoSession objects.
void Transfer(const GeckoSession::Window::LocalRef& inst,
jni::Object::Param aQueue,
jni::Object::Param aCompositor,
jni::Object::Param aDispatcher,
- jni::Object::Param aSettings);
+ jni::Object::Param aInitData);
void AttachEditable(const GeckoSession::Window::LocalRef& inst,
jni::Object::Param aEditableParent,
jni::Object::Param aEditableChild);
void OnReady(jni::Object::Param aQueue = nullptr);
};
@@ -758,26 +758,24 @@ nsWindow::NativePtr<nsWindow::NPZCSuppor
bool nsWindow::NPZCSupport::sNegateWheelScroll;
NS_IMPL_ISUPPORTS(nsWindow::AndroidView,
nsIAndroidEventDispatcher,
nsIAndroidView)
nsresult
-nsWindow::AndroidView::GetSettings(JSContext* aCx, JS::MutableHandleValue aOut)
+nsWindow::AndroidView::GetInitData(JSContext* aCx, JS::MutableHandleValue aOut)
{
- if (!mSettings) {
+ if (!mInitData) {
aOut.setNull();
return NS_OK;
}
- // Lock to prevent races with UI thread.
- auto lock = mSettings.Lock();
- return widget::EventDispatcher::UnboxBundle(aCx, mSettings, aOut);
+ return widget::EventDispatcher::UnboxBundle(aCx, mInitData, aOut);
}
/**
* Compositor has some unique requirements for its native calls, so make it
* separate from GeckoViewSupport.
*/
class nsWindow::LayerViewSupport final
: public LayerSession::Compositor::Natives<LayerViewSupport>
@@ -1166,17 +1164,17 @@ nsWindow::GeckoViewSupport::~GeckoViewSu
}
/* static */ void
nsWindow::GeckoViewSupport::Open(const jni::Class::LocalRef& aCls,
GeckoSession::Window::Param aWindow,
jni::Object::Param aQueue,
jni::Object::Param aCompositor,
jni::Object::Param aDispatcher,
- jni::Object::Param aSettings,
+ jni::Object::Param aInitData,
jni::String::Param aId,
jni::String::Param aChromeURI,
int32_t aScreenId,
bool aPrivateMode)
{
MOZ_ASSERT(NS_IsMainThread());
AUTO_PROFILER_LABEL("nsWindow::GeckoViewSupport::Open", OTHER);
@@ -1193,17 +1191,17 @@ nsWindow::GeckoViewSupport::Open(const j
url = NS_LITERAL_CSTRING("chrome://geckoview/content/geckoview.xul");
}
}
// Prepare an nsIAndroidView to pass as argument to the window.
RefPtr<AndroidView> androidView = new AndroidView();
androidView->mEventDispatcher->Attach(
java::EventDispatcher::Ref::From(aDispatcher), nullptr);
- androidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
+ androidView->mInitData = java::GeckoBundle::Ref::From(aInitData);
nsAutoCString chromeFlags("chrome,dialog=0,resizable,scrollbars");
if (aPrivateMode) {
chromeFlags += ",private";
}
nsCOMPtr<mozIDOMWindowProxy> domWindow;
ww->OpenWindow(nullptr, url.get(), aId->ToCString().get(), chromeFlags.get(),
androidView, getter_AddRefs(domWindow));
@@ -1221,17 +1219,17 @@ nsWindow::GeckoViewSupport::Open(const j
GeckoSession::Window::LocalRef sessionWindow(aCls.Env(), aWindow);
window->mGeckoViewSupport =
mozilla::MakeUnique<GeckoViewSupport>(window, sessionWindow);
window->mGeckoViewSupport->mDOMWindow = pdomWindow;
window->mAndroidView = androidView;
// Attach other session support objects.
window->mGeckoViewSupport->Transfer(
- sessionWindow, aQueue, aCompositor, aDispatcher, aSettings);
+ sessionWindow, aQueue, aCompositor, aDispatcher, aInitData);
if (window->mWidgetListener) {
nsCOMPtr<nsIXULWindow> xulWindow(
window->mWidgetListener->GetXULWindow());
if (xulWindow) {
// Our window is not intrinsically sized, so tell nsXULWindow to
// not set a size for us.
xulWindow->SetIntrinsicallySized(false);
@@ -1255,17 +1253,17 @@ nsWindow::GeckoViewSupport::Close()
mGeckoViewWindow = nullptr;
}
void
nsWindow::GeckoViewSupport::Transfer(const GeckoSession::Window::LocalRef& inst,
jni::Object::Param aQueue,
jni::Object::Param aCompositor,
jni::Object::Param aDispatcher,
- jni::Object::Param aSettings)
+ jni::Object::Param aInitData)
{
if (window.mNPZCSupport) {
MOZ_ASSERT(window.mLayerViewSupport);
window.mNPZCSupport.Detach();
}
if (window.mLayerViewSupport) {
window.mLayerViewSupport.Detach();
@@ -1273,20 +1271,24 @@ nsWindow::GeckoViewSupport::Transfer(con
auto compositor = LayerSession::Compositor::LocalRef(
inst.Env(), LayerSession::Compositor::Ref::From(aCompositor));
window.mLayerViewSupport.Attach(compositor, &window, compositor);
MOZ_ASSERT(window.mAndroidView);
window.mAndroidView->mEventDispatcher->Attach(
java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
- window.mAndroidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
if (mIsReady) {
+ // We're in a transfer; update init-data and notify JS code.
+ window.mAndroidView->mInitData =
+ java::GeckoBundle::Ref::From(aInitData);
OnReady(aQueue);
+ window.mAndroidView->mEventDispatcher->Dispatch(
+ u"GeckoView:UpdateInitData");
}
DispatchToUiThread(
"GeckoViewSupport::Transfer",
[compositor = LayerSession::Compositor::GlobalRef(compositor)] {
compositor->OnCompositorAttached();
});
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -161,17 +161,17 @@ private:
AndroidView() {}
NS_DECL_ISUPPORTS
NS_DECL_NSIANDROIDVIEW
NS_FORWARD_NSIANDROIDEVENTDISPATCHER(mEventDispatcher->)
- mozilla::java::GeckoBundle::GlobalRef mSettings;
+ mozilla::java::GeckoBundle::GlobalRef mInitData;
};
RefPtr<AndroidView> mAndroidView;
class LayerViewSupport;
// Object that implements native LayerView calls.
// Owned by the Java LayerView instance.
NativePtr<LayerViewSupport> mLayerViewSupport;