зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1343613 - [1.7] Refactor native call queuing out of GeckoThread. r=jchen
This commit is contained in:
Родитель
2cf62698de
Коммит
5db48b0409
|
@ -287,6 +287,7 @@ gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
|
|||
'gfx/VsyncSource.java',
|
||||
'InputConnectionListener.java',
|
||||
'InputMethods.java',
|
||||
'NativeQueue.java',
|
||||
'NotificationListener.java',
|
||||
'NSSBridge.java',
|
||||
'permissions/PermissionBlock.java',
|
||||
|
|
|
@ -8,12 +8,10 @@ package org.mozilla.gecko;
|
|||
import org.mozilla.gecko.annotation.RobocopTarget;
|
||||
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||
import org.mozilla.gecko.mozglue.GeckoLoader;
|
||||
import org.mozilla.gecko.NativeQueue.StateHolder;
|
||||
import org.mozilla.gecko.util.FileUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
|
@ -28,19 +26,14 @@ import android.util.Log;
|
|||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class GeckoThread extends Thread {
|
||||
private static final String LOGTAG = "GeckoThread";
|
||||
|
||||
public enum State {
|
||||
public enum State implements NativeQueue.State {
|
||||
// After being loaded by class loader.
|
||||
@WrapForJNI INITIAL(0),
|
||||
// After launching Gecko thread
|
||||
|
@ -73,47 +66,30 @@ public class GeckoThread extends Thread {
|
|||
this.rank = rank;
|
||||
}
|
||||
|
||||
public boolean is(final State other) {
|
||||
@Override
|
||||
public boolean is(final NativeQueue.State other) {
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public boolean isAtLeast(final State other) {
|
||||
return this.rank >= other.rank;
|
||||
@Override
|
||||
public boolean isAtLeast(final NativeQueue.State other) {
|
||||
if (other instanceof State) {
|
||||
return this.rank >= ((State) other).rank;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAtMost(final State other) {
|
||||
return this.rank <= other.rank;
|
||||
}
|
||||
private static final StateHolder sStateHolder =
|
||||
new StateHolder(State.INITIAL, State.RUNNING);
|
||||
|
||||
// Inclusive
|
||||
public boolean isBetween(final State min, final State max) {
|
||||
return this.rank >= min.rank && this.rank <= max.rank;
|
||||
}
|
||||
/* package */ static StateHolder getStateHolder() {
|
||||
return sStateHolder;
|
||||
}
|
||||
|
||||
public static final State MIN_STATE = State.INITIAL;
|
||||
public static final State MAX_STATE = State.EXITED;
|
||||
|
||||
private static volatile State sState = State.INITIAL;
|
||||
|
||||
private static class QueuedCall {
|
||||
public Method method;
|
||||
public Object target;
|
||||
public Object[] args;
|
||||
public State state;
|
||||
|
||||
public QueuedCall(final Method method, final Object target,
|
||||
final Object[] args, final State state) {
|
||||
this.method = method;
|
||||
this.target = target;
|
||||
this.args = args;
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int QUEUED_CALLS_COUNT = 16;
|
||||
private static final ArrayList<QueuedCall> QUEUED_CALLS = new ArrayList<>(QUEUED_CALLS_COUNT);
|
||||
|
||||
private static final Runnable UI_THREAD_CALLBACK = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -258,153 +234,6 @@ public class GeckoThread extends Thread {
|
|||
return isState(State.RUNNING);
|
||||
}
|
||||
|
||||
// Invoke the given Method and handle checked Exceptions.
|
||||
private static void invokeMethod(final Method method, final Object obj, final Object[] args) {
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
method.invoke(obj, args);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new IllegalStateException("Unexpected exception", e);
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException("Cannot make call", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
// Queue a call to the given method.
|
||||
private static void queueNativeCallLocked(final Class<?> cls, final String methodName,
|
||||
final Object obj, final Object[] args,
|
||||
final State state) {
|
||||
final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
|
||||
final ArrayList<Object> argValues = new ArrayList<>(args.length);
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (args[i] instanceof Class) {
|
||||
argTypes.add((Class<?>) args[i]);
|
||||
argValues.add(args[++i]);
|
||||
continue;
|
||||
}
|
||||
Class<?> argType = args[i].getClass();
|
||||
if (argType == Boolean.class) argType = Boolean.TYPE;
|
||||
else if (argType == Byte.class) argType = Byte.TYPE;
|
||||
else if (argType == Character.class) argType = Character.TYPE;
|
||||
else if (argType == Double.class) argType = Double.TYPE;
|
||||
else if (argType == Float.class) argType = Float.TYPE;
|
||||
else if (argType == Integer.class) argType = Integer.TYPE;
|
||||
else if (argType == Long.class) argType = Long.TYPE;
|
||||
else if (argType == Short.class) argType = Short.TYPE;
|
||||
argTypes.add(argType);
|
||||
argValues.add(args[i]);
|
||||
}
|
||||
final Method method;
|
||||
try {
|
||||
method = cls.getDeclaredMethod(
|
||||
methodName, argTypes.toArray(new Class<?>[argTypes.size()]));
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("Cannot find method", e);
|
||||
}
|
||||
|
||||
if (!Modifier.isNative(method.getModifiers())) {
|
||||
// As a precaution, we disallow queuing non-native methods. Queuing non-native
|
||||
// methods is dangerous because the method could end up being called on either
|
||||
// the original thread or the Gecko thread depending on timing. Native methods
|
||||
// usually handle this by posting an event to the Gecko thread automatically,
|
||||
// but there is no automatic mechanism for non-native methods.
|
||||
throw new UnsupportedOperationException("Not allowed to queue non-native methods");
|
||||
}
|
||||
|
||||
if (isStateAtLeast(state)) {
|
||||
invokeMethod(method, obj, argValues.toArray());
|
||||
return;
|
||||
}
|
||||
|
||||
QUEUED_CALLS.add(new QueuedCall(
|
||||
method, obj, argValues.toArray(), state));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given static method until Gecko is in the given state.
|
||||
*
|
||||
* @param state The Gecko state in which the native call could be executed.
|
||||
* Default is State.RUNNING, which means this queued call will
|
||||
* run when Gecko is at or after RUNNING state.
|
||||
* @param cls Class that declares the static method.
|
||||
* @param methodName Name of the static method.
|
||||
* @param args Args to call the static method with; to specify a parameter type,
|
||||
* pass in a Class instance first, followed by the value.
|
||||
*/
|
||||
public static void queueNativeCallUntil(final State state, final Class<?> cls,
|
||||
final String methodName, final Object... args) {
|
||||
synchronized (QUEUED_CALLS) {
|
||||
queueNativeCallLocked(cls, methodName, null, args, state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given static method until Gecko is in the RUNNING state.
|
||||
*/
|
||||
public static void queueNativeCall(final Class<?> cls, final String methodName,
|
||||
final Object... args) {
|
||||
synchronized (QUEUED_CALLS) {
|
||||
queueNativeCallLocked(cls, methodName, null, args, State.RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given instance method until Gecko is in the given state.
|
||||
*
|
||||
* @param state The Gecko state in which the native call could be executed.
|
||||
* @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 static void queueNativeCallUntil(final State state, final Object obj,
|
||||
final String methodName, final Object... args) {
|
||||
synchronized (QUEUED_CALLS) {
|
||||
queueNativeCallLocked(obj.getClass(), methodName, obj, args, state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given instance method until Gecko is in the RUNNING state.
|
||||
*/
|
||||
public static void queueNativeCall(final Object obj, final String methodName,
|
||||
final Object... args) {
|
||||
synchronized (QUEUED_CALLS) {
|
||||
queueNativeCallLocked(obj.getClass(), methodName, obj, args, State.RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
// Run all queued methods
|
||||
private static void flushQueuedNativeCallsLocked(final State state) {
|
||||
int lastSkipped = -1;
|
||||
for (int i = 0; i < QUEUED_CALLS.size(); i++) {
|
||||
final QueuedCall call = QUEUED_CALLS.get(i);
|
||||
if (call == null) {
|
||||
// We already handled the call.
|
||||
continue;
|
||||
}
|
||||
if (!state.isAtLeast(call.state)) {
|
||||
// The call is not ready yet; skip it.
|
||||
lastSkipped = i;
|
||||
continue;
|
||||
}
|
||||
// Mark as handled.
|
||||
QUEUED_CALLS.set(i, null);
|
||||
|
||||
invokeMethod(call.method, call.target, call.args);
|
||||
}
|
||||
if (lastSkipped < 0) {
|
||||
// We're done here; release the memory
|
||||
QUEUED_CALLS.clear();
|
||||
QUEUED_CALLS.trimToSize();
|
||||
} else if (lastSkipped < QUEUED_CALLS.size() - 1) {
|
||||
// We skipped some; free up null entries at the end,
|
||||
// but keep all the previous entries for later.
|
||||
QUEUED_CALLS.subList(lastSkipped + 1, QUEUED_CALLS.size()).clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadGeckoLibs(final Context context, final String resourcePath) {
|
||||
GeckoLoader.loadSQLiteLibs(context, resourcePath);
|
||||
GeckoLoader.loadNSSLibs(context, resourcePath);
|
||||
|
@ -616,7 +445,7 @@ public class GeckoThread extends Thread {
|
|||
* @return True if the current Gecko thread state matches
|
||||
*/
|
||||
public static boolean isState(final State state) {
|
||||
return sState.is(state);
|
||||
return sStateHolder.getState().is(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -627,7 +456,7 @@ public class GeckoThread extends Thread {
|
|||
* @return True if the current Gecko thread state matches
|
||||
*/
|
||||
public static boolean isStateAtLeast(final State state) {
|
||||
return sState.isAtLeast(state);
|
||||
return sStateHolder.getState().isAtLeast(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -638,7 +467,7 @@ public class GeckoThread extends Thread {
|
|||
* @return True if the current Gecko thread state matches
|
||||
*/
|
||||
public static boolean isStateAtMost(final State state) {
|
||||
return sState.isAtMost(state);
|
||||
return state.isAtLeast(sStateHolder.getState());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -650,28 +479,18 @@ public class GeckoThread extends Thread {
|
|||
* @return True if the current Gecko thread state matches
|
||||
*/
|
||||
public static boolean isStateBetween(final State minState, final State maxState) {
|
||||
return sState.isBetween(minState, maxState);
|
||||
return isStateAtLeast(minState) && isStateAtMost(maxState);
|
||||
}
|
||||
|
||||
@WrapForJNI(calledFrom = "gecko")
|
||||
private static void setState(final State newState) {
|
||||
ThreadUtils.assertOnGeckoThread();
|
||||
synchronized (QUEUED_CALLS) {
|
||||
flushQueuedNativeCallsLocked(newState);
|
||||
sState = newState;
|
||||
}
|
||||
sStateHolder.setState(newState);
|
||||
}
|
||||
|
||||
@WrapForJNI(calledFrom = "gecko")
|
||||
private static boolean checkAndSetState(final State currentState, final State newState) {
|
||||
synchronized (QUEUED_CALLS) {
|
||||
if (sState == currentState) {
|
||||
flushQueuedNativeCallsLocked(newState);
|
||||
sState = newState;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
private static boolean checkAndSetState(final State expectedState,
|
||||
final State newState) {
|
||||
return sStateHolder.checkAndSetState(expectedState, newState);
|
||||
}
|
||||
|
||||
@WrapForJNI(stubName = "SpeculativeConnect")
|
||||
|
@ -735,4 +554,36 @@ public class GeckoThread extends Thread {
|
|||
private static void requestUiThreadCallback(long delay) {
|
||||
ThreadUtils.getUiHandler().postDelayed(UI_THREAD_CALLBACK, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given static method until Gecko is in the RUNNING state.
|
||||
*/
|
||||
public static void queueNativeCall(final Class<?> cls, final String methodName,
|
||||
final Object... args) {
|
||||
NativeQueue.queueUntil(getStateHolder(), State.RUNNING, cls, methodName, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given instance method until Gecko is in the RUNNING state.
|
||||
*/
|
||||
public static void queueNativeCall(final Object obj, final String methodName,
|
||||
final Object... args) {
|
||||
NativeQueue.queueUntil(getStateHolder(), State.RUNNING, obj, methodName, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given instance method until Gecko is in the RUNNING state.
|
||||
*/
|
||||
public static void queueNativeCallUntil(final State state, final Object obj, final String methodName,
|
||||
final Object... args) {
|
||||
NativeQueue.queueUntil(getStateHolder(), state, obj, methodName, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given static method until Gecko is in the RUNNING state.
|
||||
*/
|
||||
public static void queueNativeCallUntil(final State state, final Class<?> cls, final String methodName,
|
||||
final Object... args) {
|
||||
NativeQueue.queueUntil(getStateHolder(), state, cls, methodName, args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.mozilla.gecko.annotation.ReflectionTarget;
|
|||
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.mozglue.JNIObject;
|
||||
import org.mozilla.gecko.NativeQueue.StateHolder;
|
||||
import org.mozilla.gecko.util.BundleEventListener;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.GeckoBundle;
|
||||
|
@ -41,6 +42,38 @@ public class GeckoView extends LayerView
|
|||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/* package */ enum State implements NativeQueue.State {
|
||||
@WrapForJNI INITIAL(0),
|
||||
@WrapForJNI READY(1);
|
||||
|
||||
private int rank;
|
||||
|
||||
private State(int rank) {
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is(final NativeQueue.State other) {
|
||||
return this == other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAtLeast(final NativeQueue.State other) {
|
||||
if (other instanceof State) {
|
||||
return this.rank >= ((State) other).rank;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final StateHolder mStateHolder =
|
||||
new StateHolder(State.INITIAL, State.READY);
|
||||
|
||||
@WrapForJNI(calledFrom = "gecko")
|
||||
private void setState(final State newState) {
|
||||
mStateHolder.setState(newState);
|
||||
}
|
||||
|
||||
private final EventDispatcher mEventDispatcher = new EventDispatcher();
|
||||
|
||||
private ChromeDelegate mChromeDelegate;
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class NativeQueue {
|
||||
private static final String LOGTAG = "GeckoNativeQueue";
|
||||
|
||||
public interface State {
|
||||
boolean is(final State other);
|
||||
boolean isAtLeast(final State other);
|
||||
}
|
||||
|
||||
public static class StateHolder {
|
||||
private volatile State mState;
|
||||
private final State mReadyState;
|
||||
|
||||
public StateHolder(final State initial, final State ready) {
|
||||
this.mState = initial;
|
||||
this.mReadyState = ready;
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return getState().isAtLeast(mReadyState);
|
||||
}
|
||||
|
||||
public State getReadyState() {
|
||||
return mReadyState;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
public boolean setState(final State newState) {
|
||||
return checkAndSetState(null, newState);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class QueuedCall {
|
||||
public Method method;
|
||||
public Object target;
|
||||
public Object[] args;
|
||||
public State state;
|
||||
|
||||
public QueuedCall(final Method method, final Object target,
|
||||
final Object[] args, final State state) {
|
||||
this.method = method;
|
||||
this.target = target;
|
||||
this.args = args;
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int QUEUED_CALLS_COUNT = 16;
|
||||
/* package */ static final ArrayList<QueuedCall> sQueue =
|
||||
new ArrayList<>(QUEUED_CALLS_COUNT);
|
||||
|
||||
// Invoke the given Method and handle checked Exceptions.
|
||||
private static void invokeMethod(final Method method, final Object obj,
|
||||
final Object[] args) {
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
method.invoke(obj, args);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new IllegalStateException("Unexpected exception", e);
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException("Cannot make call", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
// Queue a call to the given method.
|
||||
private static void queueNativeCallLocked(final StateHolder stateHolder,
|
||||
final Class<?> cls,
|
||||
final String methodName,
|
||||
final Object obj,
|
||||
final Object[] args,
|
||||
final State state) {
|
||||
final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
|
||||
final ArrayList<Object> argValues = new ArrayList<>(args.length);
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (args[i] instanceof Class) {
|
||||
argTypes.add((Class<?>) args[i]);
|
||||
argValues.add(args[++i]);
|
||||
continue;
|
||||
}
|
||||
Class<?> argType = args[i].getClass();
|
||||
if (argType == Boolean.class) argType = Boolean.TYPE;
|
||||
else if (argType == Byte.class) argType = Byte.TYPE;
|
||||
else if (argType == Character.class) argType = Character.TYPE;
|
||||
else if (argType == Double.class) argType = Double.TYPE;
|
||||
else if (argType == Float.class) argType = Float.TYPE;
|
||||
else if (argType == Integer.class) argType = Integer.TYPE;
|
||||
else if (argType == Long.class) argType = Long.TYPE;
|
||||
else if (argType == Short.class) argType = Short.TYPE;
|
||||
argTypes.add(argType);
|
||||
argValues.add(args[i]);
|
||||
}
|
||||
final Method method;
|
||||
try {
|
||||
method = cls.getDeclaredMethod(
|
||||
methodName, argTypes.toArray(new Class<?>[argTypes.size()]));
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("Cannot find method", e);
|
||||
}
|
||||
|
||||
if (!Modifier.isNative(method.getModifiers())) {
|
||||
// As a precaution, we disallow queuing non-native methods. Queuing non-native
|
||||
// methods is dangerous because the method could end up being called on either
|
||||
// the original thread or the Gecko thread depending on timing. Native methods
|
||||
// usually handle this by posting an event to the Gecko thread automatically,
|
||||
// but there is no automatic mechanism for non-native methods.
|
||||
throw new UnsupportedOperationException("Not allowed to queue non-native methods");
|
||||
}
|
||||
|
||||
if (stateHolder.getState().isAtLeast(state)) {
|
||||
invokeMethod(method, obj, argValues.toArray());
|
||||
return;
|
||||
}
|
||||
|
||||
sQueue.add(new QueuedCall(
|
||||
method, obj, argValues.toArray(), state));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given instance method if the given current state does
|
||||
* 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 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 static void queueUntil(final StateHolder stateHolder,
|
||||
final State state,
|
||||
final Object obj,
|
||||
final String methodName,
|
||||
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
|
||||
* 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 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 static void queueUntil(final StateHolder stateHolder,
|
||||
final State state,
|
||||
final Class<?> cls,
|
||||
final String methodName,
|
||||
final Object... args) {
|
||||
synchronized (sQueue) {
|
||||
queueNativeCallLocked(stateHolder, cls, methodName, null, args, state);
|
||||
}
|
||||
}
|
||||
|
||||
// Run all queued methods
|
||||
private static void flushQueuedLocked(final State state) {
|
||||
int lastSkipped = -1;
|
||||
for (int i = 0; i < sQueue.size(); i++) {
|
||||
final QueuedCall call = sQueue.get(i);
|
||||
if (call == null) {
|
||||
// We already handled the call.
|
||||
continue;
|
||||
}
|
||||
if (!state.isAtLeast(call.state)) {
|
||||
// The call is not ready yet; skip it.
|
||||
lastSkipped = i;
|
||||
continue;
|
||||
}
|
||||
// Mark as handled.
|
||||
sQueue.set(i, null);
|
||||
|
||||
invokeMethod(call.method, call.target, call.args);
|
||||
}
|
||||
if (lastSkipped < 0) {
|
||||
// We're done here; release the memory
|
||||
sQueue.clear();
|
||||
sQueue.trimToSize();
|
||||
} else if (lastSkipped < sQueue.size() - 1) {
|
||||
// We skipped some; free up null entries at the end,
|
||||
// but keep all the previous entries for later.
|
||||
sQueue.subList(lastSkipped + 1, sQueue.size()).clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1000,6 +1000,33 @@ auto GeckoThread::State::RUNNING() -> State::LocalRef
|
|||
const char GeckoView::name[] =
|
||||
"org/mozilla/gecko/GeckoView";
|
||||
|
||||
constexpr char GeckoView::SetState_t::name[];
|
||||
constexpr char GeckoView::SetState_t::signature[];
|
||||
|
||||
auto GeckoView::SetState(mozilla::jni::Object::Param a0) const -> void
|
||||
{
|
||||
return mozilla::jni::Method<SetState_t>::Call(GeckoView::mCtx, nullptr, a0);
|
||||
}
|
||||
|
||||
const char GeckoView::State::name[] =
|
||||
"org/mozilla/gecko/GeckoView$State";
|
||||
|
||||
constexpr char GeckoView::State::INITIAL_t::name[];
|
||||
constexpr char GeckoView::State::INITIAL_t::signature[];
|
||||
|
||||
auto GeckoView::State::INITIAL() -> State::LocalRef
|
||||
{
|
||||
return mozilla::jni::Field<INITIAL_t>::Get(State::Context(), nullptr);
|
||||
}
|
||||
|
||||
constexpr char GeckoView::State::READY_t::name[];
|
||||
constexpr char GeckoView::State::READY_t::signature[];
|
||||
|
||||
auto GeckoView::State::READY() -> State::LocalRef
|
||||
{
|
||||
return mozilla::jni::Field<READY_t>::Get(State::Context(), nullptr);
|
||||
}
|
||||
|
||||
const char GeckoView::Window::name[] =
|
||||
"org/mozilla/gecko/GeckoView$Window";
|
||||
|
||||
|
|
|
@ -2988,14 +2988,85 @@ public:
|
|||
|
||||
explicit GeckoView(const Context& ctx) : ObjectBase<GeckoView>(ctx) {}
|
||||
|
||||
class State;
|
||||
class Window;
|
||||
|
||||
struct SetState_t {
|
||||
typedef GeckoView Owner;
|
||||
typedef void ReturnType;
|
||||
typedef void SetterType;
|
||||
typedef mozilla::jni::Args<
|
||||
mozilla::jni::Object::Param> Args;
|
||||
static constexpr char name[] = "setState";
|
||||
static constexpr char signature[] =
|
||||
"(Lorg/mozilla/gecko/GeckoView$State;)V";
|
||||
static const bool isStatic = false;
|
||||
static const mozilla::jni::ExceptionMode exceptionMode =
|
||||
mozilla::jni::ExceptionMode::ABORT;
|
||||
static const mozilla::jni::CallingThread callingThread =
|
||||
mozilla::jni::CallingThread::GECKO;
|
||||
static const mozilla::jni::DispatchTarget dispatchTarget =
|
||||
mozilla::jni::DispatchTarget::CURRENT;
|
||||
};
|
||||
|
||||
auto SetState(mozilla::jni::Object::Param) const -> void;
|
||||
|
||||
static const int32_t LOAD_DEFAULT = 0;
|
||||
|
||||
static const int32_t LOAD_NEW_TAB = 1;
|
||||
|
||||
static const int32_t LOAD_SWITCH_TAB = 2;
|
||||
|
||||
static const mozilla::jni::CallingThread callingThread =
|
||||
mozilla::jni::CallingThread::GECKO;
|
||||
|
||||
};
|
||||
|
||||
class GeckoView::State : public mozilla::jni::ObjectBase<State>
|
||||
{
|
||||
public:
|
||||
static const char name[];
|
||||
|
||||
explicit State(const Context& ctx) : ObjectBase<State>(ctx) {}
|
||||
|
||||
struct INITIAL_t {
|
||||
typedef State Owner;
|
||||
typedef State::LocalRef ReturnType;
|
||||
typedef State::Param SetterType;
|
||||
typedef mozilla::jni::Args<> Args;
|
||||
static constexpr char name[] = "INITIAL";
|
||||
static constexpr char signature[] =
|
||||
"Lorg/mozilla/gecko/GeckoView$State;";
|
||||
static const bool isStatic = true;
|
||||
static const mozilla::jni::ExceptionMode exceptionMode =
|
||||
mozilla::jni::ExceptionMode::ABORT;
|
||||
static const mozilla::jni::CallingThread callingThread =
|
||||
mozilla::jni::CallingThread::ANY;
|
||||
static const mozilla::jni::DispatchTarget dispatchTarget =
|
||||
mozilla::jni::DispatchTarget::CURRENT;
|
||||
};
|
||||
|
||||
static auto INITIAL() -> State::LocalRef;
|
||||
|
||||
struct READY_t {
|
||||
typedef State Owner;
|
||||
typedef State::LocalRef ReturnType;
|
||||
typedef State::Param SetterType;
|
||||
typedef mozilla::jni::Args<> Args;
|
||||
static constexpr char name[] = "READY";
|
||||
static constexpr char signature[] =
|
||||
"Lorg/mozilla/gecko/GeckoView$State;";
|
||||
static const bool isStatic = true;
|
||||
static const mozilla::jni::ExceptionMode exceptionMode =
|
||||
mozilla::jni::ExceptionMode::ABORT;
|
||||
static const mozilla::jni::CallingThread callingThread =
|
||||
mozilla::jni::CallingThread::ANY;
|
||||
static const mozilla::jni::DispatchTarget dispatchTarget =
|
||||
mozilla::jni::DispatchTarget::CURRENT;
|
||||
};
|
||||
|
||||
static auto READY() -> State::LocalRef;
|
||||
|
||||
static const mozilla::jni::CallingThread callingThread =
|
||||
mozilla::jni::CallingThread::ANY;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче