Bug 1352534 - [1.3] Allow for mupltiple native queues and assign a unique queue per GeckoView. r=jchen

This commit is contained in:
Eugen Sawin 2017-03-31 21:30:16 +02:00
Родитель 75452c87a4
Коммит 2b30df194c
4 изменённых файлов: 114 добавлений и 113 удалений

Просмотреть файл

@ -9,7 +9,6 @@ import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.mozglue.JNIObject; import org.mozilla.gecko.mozglue.JNIObject;
import org.mozilla.gecko.NativeQueue.StateHolder;
import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.GeckoBundle;
@ -55,7 +54,7 @@ public final class EventDispatcher extends JNIObject {
new HashMap<String, List<BundleEventListener>>(DEFAULT_BACKGROUND_EVENTS_COUNT); new HashMap<String, List<BundleEventListener>>(DEFAULT_BACKGROUND_EVENTS_COUNT);
private boolean mAttachedToGecko; private boolean mAttachedToGecko;
private volatile StateHolder mStateHolder; private final NativeQueue mNativeQueue;
@ReflectionTarget @ReflectionTarget
@WrapForJNI(calledFrom = "gecko") @WrapForJNI(calledFrom = "gecko")
@ -64,22 +63,15 @@ public final class EventDispatcher extends JNIObject {
} }
/* package */ EventDispatcher() { /* package */ EventDispatcher() {
mStateHolder = GeckoThread.getStateHolder(); mNativeQueue = GeckoThread.getNativeQueue();
} }
/* package */ EventDispatcher(final NativeQueue.StateHolder stateHolder) { /* package */ EventDispatcher(final NativeQueue queue) {
mStateHolder = stateHolder; mNativeQueue = queue;
}
/* package */ void setStateHolder(final NativeQueue.StateHolder stateHolder) {
mStateHolder = stateHolder;
// Force queue flushing.
final NativeQueue.State state = mStateHolder.getState();
mStateHolder.checkAndSetState(state, state);
} }
private boolean isReadyForDispatchingToGecko() { private boolean isReadyForDispatchingToGecko() {
return mStateHolder.isReady(); return mNativeQueue.isReady();
} }
@WrapForJNI(dispatchTo = "gecko") @Override // JNIObject @WrapForJNI(dispatchTo = "gecko") @Override // JNIObject
@ -301,8 +293,7 @@ public final class EventDispatcher extends JNIObject {
// Gecko, we make a special exception to queue this event until // Gecko, we make a special exception to queue this event until
// Gecko(View) is ready. This way, Gecko can first register its // Gecko(View) is ready. This way, Gecko can first register its
// listeners, and accept the event when it is ready. // listeners, and accept the event when it is ready.
NativeQueue.queueUntil(mStateHolder, mNativeQueue.queueUntilReady(this, "dispatchToGecko",
mStateHolder.getReadyState(), this, "dispatchToGecko",
String.class, type, String.class, type,
GeckoBundle.class, message, GeckoBundle.class, message,
EventCallback.class, JavaCallbackDelegate.wrap(callback)); EventCallback.class, JavaCallbackDelegate.wrap(callback));

Просмотреть файл

@ -8,7 +8,6 @@ package org.mozilla.gecko;
import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.mozglue.GeckoLoader; import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.NativeQueue.StateHolder;
import org.mozilla.gecko.util.FileUtils; import org.mozilla.gecko.util.FileUtils;
import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.ThreadUtils;
@ -80,11 +79,11 @@ public class GeckoThread extends Thread {
} }
} }
private static final StateHolder sStateHolder = private static final NativeQueue sNativeQueue =
new StateHolder(State.INITIAL, State.RUNNING); new NativeQueue(State.INITIAL, State.RUNNING);
/* package */ static StateHolder getStateHolder() { /* package */ static NativeQueue getNativeQueue() {
return sStateHolder; return sNativeQueue;
} }
public static final State MIN_STATE = State.INITIAL; public static final State MIN_STATE = State.INITIAL;
@ -446,7 +445,7 @@ public class GeckoThread extends Thread {
* @return True if the current Gecko thread state matches * @return True if the current Gecko thread state matches
*/ */
public static boolean isState(final State state) { public static boolean isState(final State state) {
return sStateHolder.getState().is(state); return sNativeQueue.getState().is(state);
} }
/** /**
@ -457,7 +456,7 @@ public class GeckoThread extends Thread {
* @return True if the current Gecko thread state matches * @return True if the current Gecko thread state matches
*/ */
public static boolean isStateAtLeast(final State state) { public static boolean isStateAtLeast(final State state) {
return sStateHolder.getState().isAtLeast(state); return sNativeQueue.getState().isAtLeast(state);
} }
/** /**
@ -468,7 +467,7 @@ public class GeckoThread extends Thread {
* @return True if the current Gecko thread state matches * @return True if the current Gecko thread state matches
*/ */
public static boolean isStateAtMost(final State state) { public static boolean isStateAtMost(final State state) {
return state.isAtLeast(sStateHolder.getState()); return state.isAtLeast(sNativeQueue.getState());
} }
/** /**
@ -485,13 +484,13 @@ public class GeckoThread extends Thread {
@WrapForJNI(calledFrom = "gecko") @WrapForJNI(calledFrom = "gecko")
private static void setState(final State newState) { private static void setState(final State newState) {
sStateHolder.setState(newState); sNativeQueue.setState(newState);
} }
@WrapForJNI(calledFrom = "gecko") @WrapForJNI(calledFrom = "gecko")
private static boolean checkAndSetState(final State expectedState, private static boolean checkAndSetState(final State expectedState,
final State newState) { final State newState) {
return sStateHolder.checkAndSetState(expectedState, newState); return sNativeQueue.checkAndSetState(expectedState, newState);
} }
@WrapForJNI(stubName = "SpeculativeConnect") @WrapForJNI(stubName = "SpeculativeConnect")
@ -561,7 +560,7 @@ public class GeckoThread extends Thread {
*/ */
public static void queueNativeCall(final Class<?> cls, final String methodName, public static void queueNativeCall(final Class<?> cls, final String methodName,
final Object... args) { final Object... args) {
NativeQueue.queueUntil(getStateHolder(), State.RUNNING, cls, methodName, args); sNativeQueue.queueUntilReady(cls, methodName, args);
} }
/** /**
@ -569,7 +568,7 @@ public class GeckoThread extends Thread {
*/ */
public static void queueNativeCall(final Object obj, final String methodName, public static void queueNativeCall(final Object obj, final String methodName,
final Object... args) { final Object... args) {
NativeQueue.queueUntil(getStateHolder(), State.RUNNING, obj, methodName, args); sNativeQueue.queueUntilReady(obj, methodName, args);
} }
/** /**
@ -577,7 +576,7 @@ public class GeckoThread extends Thread {
*/ */
public static void queueNativeCallUntil(final State state, final Object obj, final String methodName, public static void queueNativeCallUntil(final State state, final Object obj, final String methodName,
final Object... args) { final Object... args) {
NativeQueue.queueUntil(getStateHolder(), state, obj, methodName, args); sNativeQueue.queueUntil(state, obj, methodName, args);
} }
/** /**
@ -585,6 +584,6 @@ public class GeckoThread extends Thread {
*/ */
public static void queueNativeCallUntil(final State state, final Class<?> cls, final String methodName, public static void queueNativeCallUntil(final State state, final Class<?> cls, final String methodName,
final Object... args) { final Object... args) {
NativeQueue.queueUntil(getStateHolder(), state, cls, methodName, args); sNativeQueue.queueUntil(state, cls, methodName, args);
} }
} }

Просмотреть файл

@ -12,7 +12,6 @@ import org.mozilla.gecko.annotation.ReflectionTarget;
import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.mozglue.JNIObject; import org.mozilla.gecko.mozglue.JNIObject;
import org.mozilla.gecko.NativeQueue.StateHolder;
import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.GeckoBundle;
@ -47,7 +46,7 @@ public class GeckoView extends LayerView
@WrapForJNI INITIAL(0), @WrapForJNI INITIAL(0),
@WrapForJNI READY(1); @WrapForJNI READY(1);
private int mRank; private final int mRank;
private State(int rank) { private State(int rank) {
mRank = rank; mRank = rank;
@ -60,18 +59,16 @@ public class GeckoView extends LayerView
@Override @Override
public boolean isAtLeast(final NativeQueue.State other) { public boolean isAtLeast(final NativeQueue.State other) {
if (other instanceof State) { return (other instanceof State) &&
return mRank >= ((State) other).mRank; mRank >= ((State) other).mRank;
}
return false;
} }
} }
private static final StateHolder sDummyStateHolder = private final NativeQueue mNativeQueue =
new StateHolder(State.INITIAL, State.READY); new NativeQueue(State.INITIAL, State.READY);
private final EventDispatcher mEventDispatcher = private final EventDispatcher mEventDispatcher =
new EventDispatcher(sDummyStateHolder); new EventDispatcher(mNativeQueue);
private ChromeDelegate mChromeDelegate; private ChromeDelegate mChromeDelegate;
/* package */ ContentListener mContentListener; /* package */ ContentListener mContentListener;
@ -88,11 +85,12 @@ public class GeckoView extends LayerView
@WrapForJNI(dispatchTo = "proxy") @WrapForJNI(dispatchTo = "proxy")
protected static final class Window extends JNIObject { protected static final class Window extends JNIObject {
@WrapForJNI(skip = true) @WrapForJNI(skip = true)
/* package */ final StateHolder mStateHolder = /* package */ NativeQueue mNativeQueue;
new StateHolder(State.INITIAL, State.READY);
@WrapForJNI(skip = true) @WrapForJNI(skip = true)
/* package */ Window() {} /* package */ Window(final NativeQueue queue) {
mNativeQueue = queue;
}
static native void open(Window instance, GeckoView view, static native void open(Window instance, GeckoView view,
Object compositor, EventDispatcher dispatcher, Object compositor, EventDispatcher dispatcher,
@ -109,8 +107,8 @@ public class GeckoView extends LayerView
native void loadUri(String uri, int flags); native void loadUri(String uri, int flags);
@WrapForJNI(calledFrom = "gecko") @WrapForJNI(calledFrom = "gecko")
private void setState(final State newState) { private synchronized void setState(final State newState) {
mStateHolder.setState(newState); mNativeQueue.setState(newState);
} }
} }
@ -287,7 +285,12 @@ public class GeckoView extends LayerView
} }
protected void reattachWindow() { protected void reattachWindow() {
mEventDispatcher.setStateHolder(mWindow.mStateHolder); synchronized (mWindow) {
if (mNativeQueue != mWindow.mNativeQueue) {
mNativeQueue.setState(mWindow.mNativeQueue.getState());
mWindow.mNativeQueue = mNativeQueue;
}
}
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
mWindow.reattach(this, getCompositor(), mEventDispatcher); mWindow.reattach(this, getCompositor(), mEventDispatcher);
@ -304,8 +307,7 @@ public class GeckoView extends LayerView
if (mWindow == null) { if (mWindow == null) {
// Open a new nsWindow if we didn't have one from before. // Open a new nsWindow if we didn't have one from before.
mWindow = new Window(); mWindow = new Window(mNativeQueue);
mEventDispatcher.setStateHolder(mWindow.mStateHolder);
openWindow(); openWindow();
} else { } else {
reattachWindow(); reattachWindow();
@ -336,7 +338,6 @@ public class GeckoView extends LayerView
mWindow, "disposeNative"); mWindow, "disposeNative");
} }
mEventDispatcher.setStateHolder(sDummyStateHolder);
mOnAttachedToWindowCalled = false; mOnAttachedToWindowCalled = false;
} }

Просмотреть файл

@ -20,42 +20,34 @@ public class NativeQueue {
boolean isAtLeast(final State other); boolean isAtLeast(final State other);
} }
public static class StateHolder { private volatile State mState;
private volatile State mState; private final State mReadyState;
private final State mReadyState;
public StateHolder(final State initial, final State ready) { public NativeQueue(final State initial, final State ready) {
this.mState = initial; mState = initial;
this.mReadyState = ready; mReadyState = ready;
} }
public boolean isReady() { public boolean isReady() {
return getState().isAtLeast(mReadyState); return getState().isAtLeast(mReadyState);
} }
public State getReadyState() { public State getState() {
return mReadyState; return mState;
} }
public State getState() { public boolean setState(final State newState) {
return mState; return checkAndSetState(null, newState);
} }
public boolean setState(final State newState) { public synchronized boolean checkAndSetState(final State expectedState,
return checkAndSetState(null, newState); final State newState) {
} if (expectedState != null && !mState.is(expectedState)) {
return false;
public boolean checkAndSetState(final State expectedState,
final State newState) {
synchronized (NativeQueue.sQueue) {
if (expectedState != null && !mState.is(expectedState)) {
return false;
}
NativeQueue.flushQueuedLocked(newState);
mState = newState;
return true;
}
} }
flushQueuedLocked(newState);
mState = newState;
return true;
} }
private static class QueuedCall { private static class QueuedCall {
@ -74,7 +66,7 @@ public class NativeQueue {
} }
private static final int QUEUED_CALLS_COUNT = 16; private static final int QUEUED_CALLS_COUNT = 16;
/* package */ static final ArrayList<QueuedCall> sQueue = /* package */ final ArrayList<QueuedCall> mQueue =
new ArrayList<>(QUEUED_CALLS_COUNT); new ArrayList<>(QUEUED_CALLS_COUNT);
// Invoke the given Method and handle checked Exceptions. // Invoke the given Method and handle checked Exceptions.
@ -91,12 +83,11 @@ public class NativeQueue {
} }
// Queue a call to the given method. // Queue a call to the given method.
private static void queueNativeCallLocked(final StateHolder stateHolder, private void queueNativeCallLocked(final Class<?> cls,
final Class<?> cls, final String methodName,
final String methodName, final Object obj,
final Object obj, final Object[] args,
final Object[] args, final State state) {
final State state) {
final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length); final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
final ArrayList<Object> argValues = new ArrayList<>(args.length); final ArrayList<Object> argValues = new ArrayList<>(args.length);
@ -135,63 +126,82 @@ public class NativeQueue {
throw new UnsupportedOperationException("Not allowed to queue non-native methods"); throw new UnsupportedOperationException("Not allowed to queue non-native methods");
} }
if (stateHolder.getState().isAtLeast(state)) { if (getState().isAtLeast(state)) {
invokeMethod(method, obj, argValues.toArray()); invokeMethod(method, obj, argValues.toArray());
return; return;
} }
sQueue.add(new QueuedCall( mQueue.add(new QueuedCall(
method, obj, argValues.toArray(), state)); method, obj, argValues.toArray(), state));
} }
/**
* Queue a call to the given instance method if the given current state does
* not satisfy the isReady condition.
*
* @param obj Object that declares the instance method.
* @param methodName Name of the instance method.
* @param args Args to call the instance method with; to specify a parameter
* type, pass in a Class instance first, followed by the value.
*/
public synchronized void queueUntilReady(final Object obj,
final String methodName,
final Object... args) {
queueNativeCallLocked(obj.getClass(), methodName, obj, args, mReadyState);
}
/**
* Queue a call to the given static method if the given current state does
* not satisfy the isReady condition.
*
* @param cls Class that declares the static method.
* @param methodName Name of the instance method.
* @param args Args to call the instance method with; to specify a parameter
* type, pass in a Class instance first, followed by the value.
*/
public synchronized void queueUntilReady(final Class<?> cls,
final String methodName,
final Object... args) {
queueNativeCallLocked(cls, methodName, null, args, mReadyState);
}
/** /**
* Queue a call to the given instance method if the given current state does * Queue a call to the given instance method if the given current state does
* not satisfy the given state. * not satisfy the given state.
* *
* @param stateHolder The state holder used to query the current state.
* @param state The state in which the native call could be executed. * @param state The state in which the native call could be executed.
* @param obj Object that declares the instance method. * @param obj Object that declares the instance method.
* @param methodName Name of the instance method. * @param methodName Name of the instance method.
* @param args Args to call the instance method with; to specify a parameter * @param args Args to call the instance method with; to specify a parameter
* type, pass in a Class instance first, followed by the value. * type, pass in a Class instance first, followed by the value.
*/ */
public static void queueUntil(final StateHolder stateHolder, public synchronized void queueUntil(final State state, final Object obj,
final State state, final String methodName,
final Object obj, final Object... args) {
final String methodName, queueNativeCallLocked(obj.getClass(), methodName, obj, args, state);
final Object... args) {
synchronized (sQueue) {
queueNativeCallLocked(stateHolder, obj.getClass(), methodName, obj,
args, state);
}
} }
/** /**
* Queue a call to the given static method if the given current state does * Queue a call to the given static method if the given current state does
* not satisfy the given state. * not satisfy the given state.
* *
* @param stateHolder The state holder used to query the current state.
* @param state The state in which the native call could be executed. * @param state The state in which the native call could be executed.
* @param cls Class that declares the static method. * @param cls Class that declares the static method.
* @param methodName Name of the instance method. * @param methodName Name of the instance method.
* @param args Args to call the instance method with; to specify a parameter * @param args Args to call the instance method with; to specify a parameter
* type, pass in a Class instance first, followed by the value. * type, pass in a Class instance first, followed by the value.
*/ */
public static void queueUntil(final StateHolder stateHolder, public synchronized void queueUntil(final State state, final Class<?> cls,
final State state, final String methodName,
final Class<?> cls, final Object... args) {
final String methodName, queueNativeCallLocked(cls, methodName, null, args, state);
final Object... args) {
synchronized (sQueue) {
queueNativeCallLocked(stateHolder, cls, methodName, null, args, state);
}
} }
// Run all queued methods // Run all queued methods
private static void flushQueuedLocked(final State state) { private void flushQueuedLocked(final State state) {
int lastSkipped = -1; int lastSkipped = -1;
for (int i = 0; i < sQueue.size(); i++) { for (int i = 0; i < mQueue.size(); i++) {
final QueuedCall call = sQueue.get(i); final QueuedCall call = mQueue.get(i);
if (call == null) { if (call == null) {
// We already handled the call. // We already handled the call.
continue; continue;
@ -202,18 +212,18 @@ public class NativeQueue {
continue; continue;
} }
// Mark as handled. // Mark as handled.
sQueue.set(i, null); mQueue.set(i, null);
invokeMethod(call.method, call.target, call.args); invokeMethod(call.method, call.target, call.args);
} }
if (lastSkipped < 0) { if (lastSkipped < 0) {
// We're done here; release the memory // We're done here; release the memory
sQueue.clear(); mQueue.clear();
sQueue.trimToSize(); mQueue.trimToSize();
} else if (lastSkipped < sQueue.size() - 1) { } else if (lastSkipped < mQueue.size() - 1) {
// We skipped some; free up null entries at the end, // We skipped some; free up null entries at the end,
// but keep all the previous entries for later. // but keep all the previous entries for later.
sQueue.subList(lastSkipped + 1, sQueue.size()).clear(); mQueue.subList(lastSkipped + 1, mQueue.size()).clear();
} }
} }
} }