Bug 1343075 - Use GeckoEditableSupport from PuppetWidget; r=masayuki r=rbarker r=snorp r=esawin

Bug 1343075 - 1a. Add TextEventDispatcherListener::GetIMEUpdatePreference; r=masayuki

Add a GetIMEUpdatePreference method to TextEventDispatcherListener to
optionally control which IME notifications are received by NotifyIME.
This patch also makes nsBaseWidget forward its GetIMEUpdatePreference
call to the widget's native TextEventDispatcherListener.

Bug 1343075 - 1b. Implement GetIMEUpdatePreference for all TextEventDispatcherListener; r=masayuki

This patch implements GetIMEUpdatePreference for all
TextEventDispatcherListener implementations, by moving previous
implementations of nsIWidget::GetIMEUpdatePreference.

Bug 1343075 - 2. Allow setting a PuppetWidget's native TextEventDispatcherListener; r=masayuki

In PuppetWidget, add getter and setter for the widget's native
TextEventDispatcherListener. This allows overriding of PuppetWidget's
default IME handling. For example, on Android, the PuppetWidget's native
TextEventDispatcherListener will communicate directly with Java IME code
in the main process.

Bug 1343075 - 3. Add AIDL interface for main process; r=rbarker

Add AIDL definition and implementation for an interface for the main
process that child processes can access.

Bug 1343075 - 4. Set Gecko thread JNIEnv for child process; r=snorp

Add a JNIEnv* parameter to XRE_SetAndroidChildFds, which is used to set
the Gecko thread JNIEnv for child processes. XRE_SetAndroidChildFds is
the only Android-specific entry point for child processes, so I think
it's the most logical place to initialize JNI.

Bug 1343075 - 5. Support multiple remote GeckoEditableChild; r=esawin

Support remote GeckoEditableChild instances that are created in the
content processes and connect to the parent process GeckoEditableParent
through binders.

Support having multiple GeckoEditableChild instances in GeckoEditable by
keeping track of which child is currently focused, and only allow
calls to/from the focused child by using access tokens.

Bug 1343075 - 6. Add method to get GeckoEditableParent instance; r=esawin

Add IProcessManager.getEditableParent, which a content process can call
to get the GeckoEditableParent instance that corresponds to a given
content process tab, from the main process.

Bug 1343075 - 7. Support GeckoEditableSupport in content processes; r=esawin

Support creating and running GeckoEditableSupport attached to a
PuppetWidget in content processes.

Because we don't know PuppetWidget's lifetime as well as nsWindow's,
when attached to PuppetWidget, we need to attach/detach our native
object on focus/blur, respectively.

Bug 1343075 - 8. Connect GeckoEditableSupport on PuppetWidget creation; r=esawin

Listen to the "tab-child-created" notification and attach our content
process GeckoEditableSupport to the new PuppetWidget.

Bug 1343075 - 9. Update auto-generated bindings; r=me
This commit is contained in:
Jim Chen 2017-03-07 22:34:39 -05:00
parent d5457902e2
commit 53a1107cd1
46 changed files with 632 additions and 151 deletions

View File

@ -698,6 +698,13 @@ TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
}
}
NS_IMETHODIMP_(nsIMEUpdatePreference)
TextInputProcessor::GetIMEUpdatePreference()
{
// TextInputProcessor::NotifyIME does not require extra change notifications.
return nsIMEUpdatePreference();
}
NS_IMETHODIMP_(void)
TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
{

View File

@ -32,6 +32,9 @@ public:
// TextEventDispatcherListener
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
const IMENotification& aNotification) override;
NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
NS_IMETHOD_(void)
OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;

View File

@ -579,6 +579,7 @@ GECKOVIEW_AIDLS = \
org/mozilla/gecko/IGeckoEditableChild.aidl \
org/mozilla/gecko/IGeckoEditableParent.aidl \
org/mozilla/gecko/process/IChildProcess.aidl \
org/mozilla/gecko/process/IProcessManager.aidl \
$(NULL)
geckoview_aidl_src_path := $(topsrcdir)/mobile/android/geckoview/src/main/aidl

View File

@ -1191,4 +1191,5 @@ gvjar.sources += ['generated/org/mozilla/gecko/' + x for x in [
'IGeckoEditableChild.java',
'IGeckoEditableParent.java',
'process/IChildProcess.java',
'process/IProcessManager.java',
]]

View File

@ -4,6 +4,7 @@
package org.mozilla.gecko;
import android.os.IBinder;
import android.view.KeyEvent;
import org.mozilla.gecko.IGeckoEditableChild;
@ -11,20 +12,21 @@ import org.mozilla.gecko.IGeckoEditableChild;
// Interface for GeckoEditable calls from child to parent
interface IGeckoEditableParent {
// Notify an IME event of a type defined in GeckoEditableListener.
void notifyIME(int type);
void notifyIME(IGeckoEditableChild child, int type);
// Notify a change in editor state or type.
void notifyIMEContext(int state, String typeHint, String modeHint, String actionHint);
// Notify a change in editor selection.
void onSelectionChange(int start, int end);
void onSelectionChange(IBinder token, int start, int end);
// Notify a change in editor text.
void onTextChange(in CharSequence text, int start, int unboundedOldEnd);
void onTextChange(IBinder token, in CharSequence text,
int start, int unboundedOldEnd);
// Perform the default action associated with a key event.
void onDefaultKeyEvent(in KeyEvent event);
void onDefaultKeyEvent(IBinder token, in KeyEvent event);
// Update the screen location of current composition.
void updateCompositionRects(in RectF[] rects);
void updateCompositionRects(IBinder token, in RectF[] rects);
}

View File

@ -3,10 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.process;
import org.mozilla.gecko.process.IProcessManager;
import android.os.ParcelFileDescriptor;
interface IChildProcess {
void stop();
int getPid();
void start(in String[] args, in ParcelFileDescriptor crashReporterPfd, in ParcelFileDescriptor ipcPfd);
void start(in IProcessManager procMan, in String[] args, in ParcelFileDescriptor crashReporterPfd, in ParcelFileDescriptor ipcPfd);
}

View File

@ -0,0 +1,11 @@
/* 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.process;
import org.mozilla.gecko.IGeckoEditableParent;
interface IProcessManager {
IGeckoEditableParent getEditableParent(long contentId, long tabId);
}

View File

@ -19,6 +19,7 @@ import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
import android.graphics.RectF;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.text.Editable;
@ -61,7 +62,11 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
private Handler mIcRunHandler;
private Handler mIcPostHandler;
/* package */ IGeckoEditableChild mEditableChild;
// Parent process child used as a default for key events.
/* package */ IGeckoEditableChild mDefaultChild; // Used by IC thread.
// Parent or content process child that has the focus.
/* package */ IGeckoEditableChild mFocusedChild; // Used by IC thread.
/* package */ IBinder mFocusedToken; // Used by Gecko/binder thread.
/* package */ GeckoEditableListener mListener;
/* package */ GeckoView mView;
@ -71,7 +76,6 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
private boolean mNeedUpdateComposition; // Used by IC thread
private boolean mSuppressKeyUp; // Used by IC thread
private boolean mGeckoFocused; // Used by Gecko thread
private boolean mIgnoreSelectionChange; // Used by Gecko thread
private static final int IME_RANGE_CARETPOSITION = 1;
@ -92,8 +96,9 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
private static final int IME_RANGE_BACKCOLOR = 4;
private static final int IME_RANGE_LINECOLOR = 8;
private void onKeyEvent(KeyEvent event, int action, int savedMetaState,
boolean isSynthesizedImeKey) throws RemoteException {
private void onKeyEvent(final IGeckoEditableChild child, KeyEvent event, int action,
int savedMetaState, boolean isSynthesizedImeKey)
throws RemoteException {
// Use a separate action argument so we can override the key's original action,
// e.g. change ACTION_MULTIPLE to ACTION_DOWN. That way we don't have to allocate
// a new key event just to change its action field.
@ -117,7 +122,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
final int keyPressMetaState = (unicodeChar >= ' ' &&
unicodeChar != unmodifiedUnicodeChar) ? unmodifiedMetaState : metaState;
mEditableChild.onKeyEvent(action, event.getKeyCode(), event.getScanCode(),
child.onKeyEvent(action, event.getKeyCode(), event.getScanCode(),
metaState, keyPressMetaState, event.getEventTime(),
domPrintableKeyValue, event.getRepeatCount(), event.getFlags(),
isSynthesizedImeKey, event);
@ -167,34 +172,26 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
public synchronized void currentReplace(final int start, final int end,
final CharSequence newText) {
if (DEBUG) {
ThreadUtils.assertOnGeckoThread();
}
// On Gecko or binder thread.
mCurrentText.replace(start, end, newText);
addCurrentChangeLocked(start, end, start + newText.length());
}
public synchronized void currentSetSelection(final int start, final int end) {
if (DEBUG) {
ThreadUtils.assertOnGeckoThread();
}
// On Gecko or binder thread.
Selection.setSelection(mCurrentText, start, end);
mCurrentSelectionChanged = true;
}
public synchronized void currentSetSpan(final Object obj, final int start,
final int end, final int flags) {
if (DEBUG) {
ThreadUtils.assertOnGeckoThread();
}
// On Gecko or binder thread.
mCurrentText.setSpan(obj, start, end, flags);
addCurrentChangeLocked(start, end, end);
}
public synchronized void currentRemoveSpan(final Object obj) {
if (DEBUG) {
ThreadUtils.assertOnGeckoThread();
}
// On Gecko or binder thread.
if (obj == null) {
mCurrentText.clearSpans();
addCurrentChangeLocked(0, mCurrentText.length(), mCurrentText.length());
@ -212,9 +209,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
// Return Spanned instead of Editable because the returned object is supposed to
// be read-only. Editing should be done through one of the current*** methods.
public Spanned getCurrentText() {
if (DEBUG) {
ThreadUtils.assertOnGeckoThread();
}
// On Gecko or binder thread.
return mCurrentText;
}
@ -438,8 +433,8 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
getConstantName(Action.class, "TYPE_", action.mType) + ")");
}
if (mListener == null) {
// We haven't initialized or we've been destroyed.
if (mFocusedChild == null || mListener == null) {
// We haven't been focused or initialized, or we've been destroyed.
return;
}
@ -458,7 +453,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
switch (action.mType) {
case Action.TYPE_EVENT:
case Action.TYPE_SET_HANDLER:
mEditableChild.onImeSynchronize();
mFocusedChild.onImeSynchronize();
break;
case Action.TYPE_SET_SPAN:
@ -472,7 +467,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
action.mSpanObject == Selection.SELECTION_START ||
action.mSpanObject == Selection.SELECTION_END);
mEditableChild.onImeSynchronize();
mFocusedChild.onImeSynchronize();
break;
case Action.TYPE_REMOVE_SPAN:
@ -482,7 +477,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
mNeedUpdateComposition |= (flags & Spanned.SPAN_INTERMEDIATE) == 0 &&
(flags & Spanned.SPAN_COMPOSING) != 0;
mEditableChild.onImeSynchronize();
mFocusedChild.onImeSynchronize();
break;
case Action.TYPE_REPLACE_TEXT:
@ -498,7 +493,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
sendCharKeyEvents(action);
}
mText.shadowReplace(action.mStart, action.mEnd, action.mSequence);
mEditableChild.onImeReplaceText(
mFocusedChild.onImeReplaceText(
action.mStart, action.mEnd, action.mSequence.toString());
break;
@ -548,7 +543,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
if (DEBUG) {
Log.d(LOGTAG, "sending: " + event);
}
onKeyEvent(event, event.getAction(),
onKeyEvent(mFocusedChild, event, event.getAction(),
/* metaState */ 0, /* isSynthesizedImeKey */ true);
}
}
@ -575,7 +570,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
@WrapForJNI(calledFrom = "gecko")
private void setDefaultEditableChild(final IGeckoEditableChild child) {
mEditableChild = child;
mDefaultChild = child;
}
@WrapForJNI(calledFrom = "gecko")
@ -701,7 +696,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
if (found) {
icSendComposition(text, selStart, selEnd, composingStart, composingEnd);
if (notifyGecko) {
mEditableChild.onImeUpdateComposition(composingStart, composingEnd);
mFocusedChild.onImeUpdateComposition(composingStart, composingEnd);
}
return true;
}
@ -709,7 +704,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
if (notifyGecko) {
// Set the selection by using a composition without ranges
mEditableChild.onImeUpdateComposition(selStart, selEnd);
mFocusedChild.onImeUpdateComposition(selStart, selEnd);
}
if (DEBUG) {
@ -733,7 +728,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
if (selEnd >= composingStart && selEnd <= composingEnd) {
mEditableChild.onImeAddCompositionRange(
mFocusedChild.onImeAddCompositionRange(
selEnd - composingStart, selEnd - composingStart,
IME_RANGE_CARETPOSITION, 0, 0, false, 0, 0, 0);
}
@ -806,7 +801,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
rangeBackColor = tp.bgColor;
}
}
mEditableChild.onImeAddCompositionRange(
mFocusedChild.onImeAddCompositionRange(
rangeStart - composingStart, rangeEnd - composingStart,
rangeType, rangeStyles, rangeLineStyle, rangeBoldLine,
rangeForeColor, rangeBackColor, rangeLineColor);
@ -838,8 +833,17 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
event-type action, and update the shadow text accordingly.
*/
try {
if (mFocusedChild == null) {
// Not focused; send simple key event to chrome window.
onKeyEvent(mDefaultChild, event, action, metaState,
/* isSynthesizedImeKey */ false);
return;
}
// Focused; key event may go to chrome window or to content window.
icMaybeSendComposition();
onKeyEvent(event, action, metaState, /* isSynthesizedImeKey */ false);
onKeyEvent(mFocusedChild, event, action, metaState,
/* isSynthesizedImeKey */ false);
icOfferAction(new Action(Action.TYPE_EVENT));
} catch (final RemoteException e) {
Log.e(LOGTAG, "Remote call failed", e);
@ -952,16 +956,16 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
@Override // GeckoEditableClient
public void requestCursorUpdates(int requestMode) {
try {
mEditableChild.onImeRequestCursorUpdates(requestMode);
if (mFocusedChild != null) {
mFocusedChild.onImeRequestCursorUpdates(requestMode);
}
} catch (final RemoteException e) {
Log.e(LOGTAG, "Remote call failed", e);
}
}
private void geckoSetIcHandler(final Handler newHandler) {
if (DEBUG) {
ThreadUtils.assertOnGeckoThread();
}
// On Gecko or binder thread.
mIcPostHandler.post(new Runnable() { // posting to old IC thread
@Override
public void run() {
@ -980,13 +984,10 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
private void geckoActionReply(final Action action) {
// On Gecko or binder thread.
if (!mGeckoFocused) {
if (DEBUG) {
Log.d(LOGTAG, "discarding stale reply");
}
if (action == null) {
Log.w(LOGTAG, "Mismatched reply");
return;
}
if (DEBUG) {
Log.d(LOGTAG, "reply: Action(" +
getConstantName(Action.class, "TYPE_", action.mType) + ")");
@ -1015,8 +1016,18 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
}
private synchronized boolean binderCheckToken(final IBinder token,
final boolean allowNull) {
// Verify that we're getting an IME notification from the currently focused child.
if (mFocusedToken == token || (mFocusedToken == null && allowNull)) {
return true;
}
Log.w(LOGTAG, "Invalid token");
return false;
}
@Override // IGeckoEditableParent
public void notifyIME(final int type) {
public void notifyIME(final IGeckoEditableChild child, final int type) {
// On Gecko or binder thread.
if (DEBUG) {
// NOTIFY_IME_REPLY_EVENT is logged separately, inside geckoActionReply()
@ -1027,7 +1038,32 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
}
if (type == GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
final IBinder token = child.asBinder();
if (type == GeckoEditableListener.NOTIFY_IME_OF_TOKEN) {
synchronized (this) {
if (mFocusedToken != null && mFocusedToken != token &&
mFocusedToken.pingBinder()) {
// Focused child already exists and is alive.
Log.w(LOGTAG, "Already focused");
return;
}
mFocusedToken = token;
return;
}
} else if (type == GeckoEditableListener.NOTIFY_IME_OPEN_VKB) {
// Always from parent process.
ThreadUtils.assertOnGeckoThread();
} else if (!binderCheckToken(token, /* allowNull */ false)) {
return;
}
if (type == GeckoEditableListener.NOTIFY_IME_OF_BLUR) {
synchronized (this) {
onTextChange(token, "", 0, Integer.MAX_VALUE);
mActions.clear();
mFocusedToken = null;
}
} else if (type == GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
geckoActionReply(mActions.poll());
if (!mActions.isEmpty()) {
// Only post to IC thread below when the queue is empty.
@ -1046,8 +1082,11 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
if (type == GeckoEditableListener.NOTIFY_IME_OF_FOCUS && mListener != null) {
mFocusedChild = child;
mNeedSync = false;
mText.syncShadowText(/* listener */ null);
} else if (type == GeckoEditableListener.NOTIFY_IME_OF_BLUR) {
mFocusedChild = null;
}
if (mListener != null) {
@ -1055,18 +1094,11 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
}
});
// Update the mGeckoFocused flag.
if (type == GeckoEditableListener.NOTIFY_IME_OF_BLUR) {
mGeckoFocused = false;
} else if (type == GeckoEditableListener.NOTIFY_IME_OF_FOCUS) {
mGeckoFocused = true;
}
}
@Override // IGeckoEditableParent
public void notifyIMEContext(final int state, final String typeHint,
final String modeHint, final String actionHint) {
final String modeHint, final String actionHint) {
// On Gecko or binder thread.
if (DEBUG) {
Log.d(LOGTAG, "notifyIMEContext(" +
@ -1074,6 +1106,10 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
", \"" + typeHint + "\", \"" + modeHint + "\", \"" + actionHint + "\")");
}
// Don't check token for notifyIMEContext, because the calls all come
// from the parent process.
ThreadUtils.assertOnGeckoThread();
mIcPostHandler.post(new Runnable() {
@Override
public void run() {
@ -1086,12 +1122,17 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
@Override // IGeckoEditableParent
public void onSelectionChange(final int start, final int end) {
public void onSelectionChange(final IBinder token,
final int start, final int end) {
// On Gecko or binder thread.
if (DEBUG) {
Log.d(LOGTAG, "onSelectionChange(" + start + ", " + end + ")");
}
if (!binderCheckToken(token, /* allowNull */ false)) {
return;
}
if (mIgnoreSelectionChange) {
mIgnoreSelectionChange = false;
} else {
@ -1112,8 +1153,8 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
@Override // IGeckoEditableParent
public void onTextChange(final CharSequence text, final int start,
final int unboundedOldEnd) {
public void onTextChange(final IBinder token, final CharSequence text,
final int start, final int unboundedOldEnd) {
// On Gecko or binder thread.
if (DEBUG) {
StringBuilder sb = new StringBuilder("onTextChange(");
@ -1122,6 +1163,10 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
Log.d(LOGTAG, sb.toString());
}
if (!binderCheckToken(token, /* allowNull */ false)) {
return;
}
final int currentLength = mText.getCurrentText().length();
final int oldEnd = unboundedOldEnd > currentLength ? currentLength : unboundedOldEnd;
final int newEnd = start + text.length();
@ -1227,7 +1272,7 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
@Override // IGeckoEditableParent
public void onDefaultKeyEvent(final KeyEvent event) {
public void onDefaultKeyEvent(final IBinder token, final KeyEvent event) {
// On Gecko or binder thread.
if (DEBUG) {
StringBuilder sb = new StringBuilder("onDefaultKeyEvent(");
@ -1239,6 +1284,11 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
Log.d(LOGTAG, sb.toString());
}
// Allow default key processing even if we're not focused.
if (!binderCheckToken(token, /* allowNull */ true)) {
return;
}
mIcPostHandler.post(new Runnable() {
@Override
public void run() {
@ -1251,12 +1301,16 @@ final class GeckoEditable extends IGeckoEditableParent.Stub
}
@Override // IGeckoEditableParent
public void updateCompositionRects(final RectF[] rects) {
public void updateCompositionRects(final IBinder token, final RectF[] rects) {
// On Gecko or binder thread.
if (DEBUG) {
Log.d(LOGTAG, "updateCompositionRects(rects.length = " + rects.length + ")");
}
if (!binderCheckToken(token, /* allowNull */ false)) {
return;
}
mIcPostHandler.post(new Runnable() {
@Override
public void run() {

View File

@ -26,13 +26,65 @@ final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild
private static final boolean DEBUG = false;
private static final String LOGTAG = "GeckoEditableChild";
private final class RemoteChild extends IGeckoEditableChild.Stub {
@Override // IGeckoEditableChild
public void onKeyEvent(int action, int keyCode, int scanCode, int metaState,
int keyPressMetaState, long time, int domPrintableKeyValue,
int repeatCount, int flags, boolean isSynthesizedImeKey,
KeyEvent event) {
GeckoEditableChild.this.onKeyEvent(
action, keyCode, scanCode, metaState, keyPressMetaState, time,
domPrintableKeyValue, repeatCount, flags, isSynthesizedImeKey, event);
}
@Override // IGeckoEditableChild
public void onImeSynchronize() {
GeckoEditableChild.this.onImeSynchronize();
}
@Override // IGeckoEditableChild
public void onImeReplaceText(int start, int end, String text) {
GeckoEditableChild.this.onImeReplaceText(start, end, text);
}
@Override // IGeckoEditableChild
public void onImeAddCompositionRange(int start, int end, int rangeType,
int rangeStyles, int rangeLineStyle,
boolean rangeBoldLine, int rangeForeColor,
int rangeBackColor, int rangeLineColor) {
GeckoEditableChild.this.onImeAddCompositionRange(
start, end, rangeType, rangeStyles, rangeLineStyle, rangeBoldLine,
rangeForeColor, rangeBackColor, rangeLineColor);
}
@Override // IGeckoEditableChild
public void onImeUpdateComposition(int start, int end) {
GeckoEditableChild.this.onImeUpdateComposition(start, end);
}
@Override // IGeckoEditableChild
public void onImeRequestCursorUpdates(int requestMode) {
GeckoEditableChild.this.onImeRequestCursorUpdates(requestMode);
}
}
private final IGeckoEditableParent mEditableParent;
private final IGeckoEditableChild mEditableChild;
private int mCurrentTextLength; // Used by Gecko thread
@WrapForJNI(calledFrom = "gecko")
/* package */ GeckoEditableChild(final IGeckoEditableParent editableParent) {
mEditableParent = editableParent;
final IBinder binder = editableParent.asBinder();
if (binder.queryLocalInterface(IGeckoEditableParent.class.getName()) != null) {
// IGeckoEditableParent is local; i.e. we're in the main process.
mEditableChild = this;
} else {
// IGeckoEditableParent is remote; i.e. we're in a content process.
mEditableChild = new RemoteChild();
}
}
@WrapForJNI(dispatchTo = "proxy") @Override // IGeckoEditableChild
@ -67,7 +119,8 @@ final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild
@Override // IInterface
public IBinder asBinder() {
return null;
// Return the GeckoEditableParent's binder as our binder for comparison purposes.
return mEditableParent.asBinder();
}
@WrapForJNI(calledFrom = "gecko")
@ -92,7 +145,7 @@ final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild
}
try {
mEditableParent.notifyIME(type);
mEditableParent.notifyIME(mEditableChild, type);
} catch (final RemoteException e) {
Log.e(LOGTAG, "Remote call failed", e);
return;
@ -130,7 +183,7 @@ final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild
throw new IllegalArgumentException("invalid selection notification range");
}
mEditableParent.onSelectionChange(start, end);
mEditableParent.onSelectionChange(mEditableChild.asBinder(), start, end);
}
@WrapForJNI(calledFrom = "gecko", exceptionMode = "ignore")
@ -166,7 +219,7 @@ final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild
mCurrentTextLength += start + text.length() - oldEnd;
// Need unboundedOldEnd so GeckoEditable can distinguish changed text vs cleared text.
mEditableParent.onTextChange(text, start, unboundedOldEnd);
mEditableParent.onTextChange(mEditableChild.asBinder(), text, start, unboundedOldEnd);
}
@WrapForJNI(calledFrom = "gecko")
@ -184,7 +237,7 @@ final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild
}
try {
mEditableParent.onDefaultKeyEvent(event);
mEditableParent.onDefaultKeyEvent(mEditableChild.asBinder(), event);
} catch (final RemoteException e) {
Log.e(LOGTAG, "Remote call failed", e);
}
@ -199,7 +252,7 @@ final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild
}
try {
mEditableParent.updateCompositionRects(rects);
mEditableParent.updateCompositionRects(mEditableChild.asBinder(), rects);
} catch (final RemoteException e) {
Log.e(LOGTAG, "Remote call failed", e);
}

View File

@ -17,6 +17,8 @@ import android.view.KeyEvent;
interface GeckoEditableListener {
// IME notification type for notifyIME(), corresponding to NotificationToIME enum in Gecko
@WrapForJNI
int NOTIFY_IME_OF_TOKEN = -3;
@WrapForJNI
int NOTIFY_IME_OPEN_VKB = -2;
@WrapForJNI
int NOTIFY_IME_REPLY_EVENT = -1;

View File

@ -141,15 +141,16 @@ public class GeckoThread extends Thread {
private boolean mDebugging;
// Child process parameters
private int mCrashFileDescriptor;
private int mIPCFileDescriptor;
private int mCrashFileDescriptor = -1;
private int mIPCFileDescriptor = -1;
GeckoThread() {
setName("Gecko");
}
private boolean isChildProcess() {
return mIPCFileDescriptor != -1;
@WrapForJNI
private static boolean isChildProcess() {
return INSTANCE.mIPCFileDescriptor != -1;
}
private synchronized boolean init(final GeckoProfile profile, final String[] args,

View File

@ -5,6 +5,8 @@
package org.mozilla.gecko.process;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.IGeckoEditableParent;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.util.ThreadUtils;
@ -25,7 +27,7 @@ import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.Map;
public final class GeckoProcessManager {
public final class GeckoProcessManager extends IProcessManager.Stub {
private static final String LOGTAG = "GeckoProcessManager";
private static final GeckoProcessManager INSTANCE = new GeckoProcessManager();
@ -33,6 +35,15 @@ public final class GeckoProcessManager {
return INSTANCE;
}
@WrapForJNI(stubName = "GetEditableParent")
private static native IGeckoEditableParent nativeGetEditableParent(long contentId,
long tabId);
@Override // IProcessManager
public IGeckoEditableParent getEditableParent(final long contentId, final long tabId) {
return nativeGetEditableParent(contentId, tabId);
}
private static final class ChildConnection implements ServiceConnection, IBinder.DeathRecipient {
public final String mType;
private boolean mWait = false;
@ -150,7 +161,7 @@ public final class GeckoProcessManager {
crashPfd = ParcelFileDescriptor.fromFd(crashFd);
}
ParcelFileDescriptor ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
connection.mChild.start(args, crashPfd, ipcPfd);
connection.mChild.start(this, args, crashPfd, ipcPfd);
if (crashPfd != null) {
crashPfd.close();
}

View File

@ -6,7 +6,9 @@
package org.mozilla.gecko.process;
import org.mozilla.gecko.annotation.JNITarget;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.IGeckoEditableParent;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.GeckoThread;
import org.mozilla.gecko.mozglue.SafeIntent;
@ -18,23 +20,35 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
public class GeckoServiceChildProcess extends Service {
static private String LOGTAG = "GeckoServiceChildProcess";
private boolean serviceStarted;
private static IProcessManager sProcessManager;
static private void stop() {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
Process.killProcess(Process.myPid());;
Process.killProcess(Process.myPid());
}
});
}
@WrapForJNI(calledFrom = "gecko")
private static IGeckoEditableParent getEditableParent(final long contentId,
final long tabId) {
try {
return sProcessManager.getEditableParent(contentId, tabId);
} catch (final RemoteException e) {
Log.e(LOGTAG, "Cannot get editable", e);
return null;
}
}
public void onCreate() {
super.onCreate();
}
@ -59,14 +73,16 @@ public class GeckoServiceChildProcess extends Service {
}
@Override
public void start(final String[] args,
public void start(final IProcessManager procMan,
final String[] args,
final ParcelFileDescriptor crashReporterPfd,
final ParcelFileDescriptor ipcPfd) {
if (serviceStarted) {
if (sProcessManager != null) {
Log.e(LOGTAG, "Attempting to start a service that has already been started.");
return;
}
serviceStarted = true;
sProcessManager = procMan;
final int crashReporterFd = crashReporterPfd != null ? crashReporterPfd.detachFd() : -1;
final int ipcFd = ipcPfd != null ? ipcPfd.detachFd() : -1;
ThreadUtils.postToUiThread(new Runnable() {

View File

@ -435,7 +435,7 @@ Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jo
gBootstrap->GeckoStart(jenv, argv, argc, sAppData);
ElfLoader::Singleton.ExpectShutdown(true);
} else {
gBootstrap->XRE_SetAndroidChildFds(crashFd, ipcFd);
gBootstrap->XRE_SetAndroidChildFds(jenv, crashFd, ipcFd);
gBootstrap->XRE_SetProcessType(argv[argc - 1]);
XREChildData childData;

View File

@ -74,8 +74,8 @@ public:
::GeckoStart(aEnv, argv, argc, aAppData);
}
virtual void XRE_SetAndroidChildFds(int aCrashFd, int aIPCFd) override {
::XRE_SetAndroidChildFds(aCrashFd, aIPCFd);
virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aCrashFd, int aIPCFd) override {
::XRE_SetAndroidChildFds(aEnv, aCrashFd, aIPCFd);
}
#endif

View File

@ -113,7 +113,7 @@ public:
#ifdef MOZ_WIDGET_ANDROID
virtual void GeckoStart(JNIEnv* aEnv, char** argv, int argc, const StaticXREAppData& aAppData) = 0;
virtual void XRE_SetAndroidChildFds(int aCrashFd, int aIPCFd) = 0;
virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aCrashFd, int aIPCFd) = 0;
#endif
#ifdef LIBFUZZER

View File

@ -52,6 +52,7 @@
#include "chrome/common/child_process.h"
#if defined(MOZ_WIDGET_ANDROID)
#include "chrome/common/ipc_channel.h"
#include "mozilla/jni/Utils.h"
#endif // defined(MOZ_WIDGET_ANDROID)
#include "mozilla/ipc/BrowserProcessSubThread.h"
@ -239,8 +240,9 @@ GeckoProcessType sChildProcessType = GeckoProcessType_Default;
#if defined(MOZ_WIDGET_ANDROID)
void
XRE_SetAndroidChildFds (int crashFd, int ipcFd)
XRE_SetAndroidChildFds (JNIEnv* env, int crashFd, int ipcFd)
{
mozilla::jni::SetGeckoThreadEnv(env);
#if defined(MOZ_CRASHREPORTER)
CrashReporter::SetNotificationPipeForChild(crashFd);
#endif // defined(MOZ_CRASHREPORTER)

View File

@ -693,6 +693,11 @@ PuppetWidget::RequestIMEToCommitComposition(bool aCancel)
nsresult
PuppetWidget::NotifyIMEInternal(const IMENotification& aIMENotification)
{
if (mNativeTextEventDispatcherListener) {
// Use mNativeTextEventDispatcherListener for IME notifications.
return NS_ERROR_NOT_IMPLEMENTED;
}
switch (aIMENotification.mMessage) {
case REQUEST_TO_COMMIT_COMPOSITION:
return RequestIMEToCommitComposition(false);
@ -857,6 +862,11 @@ PuppetWidget::NotifyIMEOfCompositionUpdate(
nsIMEUpdatePreference
PuppetWidget::GetIMEUpdatePreference()
{
if (mNativeTextEventDispatcherListener) {
// Use mNativeTextEventDispatcherListener for IME preference.
return mNativeTextEventDispatcherListener->GetIMEUpdatePreference();
}
// e10s requires IME content cache in in the TabParent for handling query
// content event only with the parent process. Therefore, this process
// needs to receive a lot of information from the focused editor to sent

View File

@ -26,6 +26,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/ContentCache.h"
#include "mozilla/EventForwards.h"
#include "mozilla/TextEventDispatcherListener.h"
namespace mozilla {
@ -183,6 +184,10 @@ public:
virtual InputContext GetInputContext() override;
virtual NativeIMEContext GetNativeIMEContext() override;
virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override
{ return mNativeTextEventDispatcherListener; }
void SetNativeTextEventDispatcherListener(TextEventDispatcherListener* aListener)
{ mNativeTextEventDispatcherListener = aListener; }
virtual void SetCursor(nsCursor aCursor) override;
virtual nsresult SetCursor(imgIContainer* aCursor,
@ -376,6 +381,8 @@ private:
nsCOMArray<nsIKeyEventInPluginCallback> mKeyEventInPluginCallbacks;
RefPtr<TextEventDispatcherListener> mNativeTextEventDispatcherListener;
protected:
bool mEnabled;
bool mVisible;

View File

@ -7,6 +7,8 @@
#include "nsWeakReference.h"
struct nsIMEUpdatePreference;
namespace mozilla {
namespace widget {
@ -30,6 +32,11 @@ public:
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
const IMENotification& aNotification) = 0;
/**
* Returns preference for which IME notification are received by NotifyIME().
*/
NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() = 0;
/**
* OnRemovedFrom() is called when the TextEventDispatcher stops working and
* is releasing the listener.

View File

@ -8,6 +8,7 @@
#include "AndroidRect.h"
#include "KeyEvent.h"
#include "PuppetWidget.h"
#include "android_npapi.h"
#include "nsIContent.h"
#include "nsISelection.h"
@ -391,7 +392,7 @@ GeckoEditableSupport::RemoveComposition(RemoveCompositionFlag aFlag)
nsEventStatus status = nsEventStatus_eIgnore;
NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
mDispatcher->CommitComposition(
status, aFlag == CANCEL_IME_COMPOSITION ? &EmptyString() : nullptr);
}
@ -442,7 +443,7 @@ GeckoEditableSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
mIMEKeyEvents.AppendElement(UniquePtr<WidgetEvent>(event.Duplicate()));
} else {
RemoveComposition();
NS_ENSURE_SUCCESS_VOID(dispatcher->BeginNativeInputTransaction());
NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(dispatcher));
dispatcher->DispatchKeyboardEvent(msg, event, status);
if (widget->Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
// Skip default processing.
@ -463,6 +464,12 @@ GeckoEditableSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
if (aIsSynthesizedImeKey) {
mIMEKeyEvents.AppendElement(
UniquePtr<WidgetEvent>(pressEvent.Duplicate()));
} else if (nsIWidget::UsePuppetWidgets()) {
AutoCacheNativeKeyCommands autoCache(
static_cast<PuppetWidget*>(widget.get()));
// Don't use native key bindings.
autoCache.CacheNoCommands();
dispatcher->MaybeDispatchKeypressEvents(pressEvent, status);
} else {
dispatcher->MaybeDispatchKeypressEvents(pressEvent, status);
}
@ -482,7 +489,7 @@ GeckoEditableSupport::SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg)
WidgetKeyboardEvent event(true, msg, aWidget);
event.mTime = PR_Now() / 1000;
MOZ_ASSERT(event.mKeyCode == 0);
NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
mDispatcher->DispatchKeyboardEvent(msg, event, status);
}
@ -757,7 +764,7 @@ GeckoEditableSupport::OnImeReplaceText(int32_t aStart, int32_t aEnd,
*/
nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE_VOID(mDispatcher && widget);
NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
RefPtr<TextComposition> composition(GetComposition());
MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
@ -792,11 +799,17 @@ GeckoEditableSupport::OnImeReplaceText(int32_t aStart, int32_t aEnd,
// widget for duplicated events is initially nullptr.
event->mWidget = widget;
if (event->mMessage == eKeyPress) {
mDispatcher->MaybeDispatchKeypressEvents(*event, status);
} else {
if (event->mMessage != eKeyPress) {
mDispatcher->DispatchKeyboardEvent(
event->mMessage, *event, status);
} else if (nsIWidget::UsePuppetWidgets()) {
AutoCacheNativeKeyCommands autoCache(
static_cast<PuppetWidget*>(widget.get()));
// Don't use native key bindings.
autoCache.CacheNoCommands();
mDispatcher->MaybeDispatchKeypressEvents(*event, status);
} else {
mDispatcher->MaybeDispatchKeypressEvents(*event, status);
}
if (widget->Destroyed()) {
break;
@ -952,7 +965,7 @@ GeckoEditableSupport::OnImeUpdateComposition(int32_t aStart, int32_t aEnd)
text, event.mData.Length(), event.mRanges->Length());
#endif // DEBUG_ANDROID_IME
NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
mDispatcher->SetPendingComposition(string, mIMERanges);
mDispatcher->FlushPendingComposition(status);
mIMERanges->Clear();
@ -1024,6 +1037,21 @@ GeckoEditableSupport::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
return;
}
mEditable->NotifyIME(
GeckoEditableListener::NOTIFY_IME_OF_TOKEN);
if (mIsRemote) {
if (!mEditableAttached) {
// Re-attach on focus; see OnRemovedFrom().
AttachNative(mEditable, this);
mEditableAttached = true;
}
// Because GeckoEditableSupport in content process doesn't
// manage the active input context, we need to retrieve the
// input context from the widget, for use by
// OnImeReplaceText.
mInputContext = widget->GetInputContext();
}
mDispatcher = dispatcher;
mIMEKeyEvents.Clear();
FlushIMEText();
@ -1043,7 +1071,7 @@ GeckoEditableSupport::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
if (!mIMEMaskEventsCount) {
mEditable->NotifyIME(GeckoEditableListener::NOTIFY_IME_OF_BLUR);
mDispatcher = nullptr;
OnRemovedFrom(mDispatcher);
}
// Mask events because we lost focus. Unmask on the next focus.
@ -1091,6 +1119,12 @@ GeckoEditableSupport::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
void
GeckoEditableSupport::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
{
mDispatcher = nullptr;
if (mIsRemote) {
// When we're remote, detach every time.
OnDetach();
}
}
void
@ -1101,7 +1135,7 @@ GeckoEditableSupport::WillDispatchKeyboardEvent(
{
}
nsIMEUpdatePreference
NS_IMETHODIMP_(nsIMEUpdatePreference)
GeckoEditableSupport::GetIMEUpdatePreference()
{
// While a plugin has focus, Listener doesn't need any notifications.

View File

@ -89,9 +89,11 @@ class GeckoEditableSupport final
COMMIT_IME_COMPOSITION
};
const bool mIsRemote;
nsWindow::WindowPtr<GeckoEditableSupport> mWindow; // Parent only
RefPtr<TextEventDispatcher> mDispatcher;
java::GeckoEditableChild::GlobalRef mEditable;
bool mEditableAttached;
InputContext mInputContext;
AutoTArray<UniquePtr<mozilla::WidgetEvent>, 4> mIMEKeyEvents;
AutoTArray<IMETextChange, 4> mIMETextChanges;
@ -107,6 +109,15 @@ class GeckoEditableSupport final
return mDispatcher ? mDispatcher->GetWidget() : mWindow;
}
nsresult BeginInputTransaction(TextEventDispatcher* aDispatcher)
{
if (mIsRemote) {
return aDispatcher->BeginInputTransaction(this);
} else {
return aDispatcher->BeginNativeInputTransaction();
}
}
virtual ~GeckoEditableSupport() {}
RefPtr<TextComposition> GetComposition() const;
@ -154,11 +165,14 @@ public:
mozilla::Move(aCall)));
}
// Constructor for main process GeckoEditableChild.
GeckoEditableSupport(nsWindow::NativePtr<GeckoEditableSupport>* aPtr,
nsWindow* aWindow,
java::GeckoEditableChild::Param aEditableChild)
: mWindow(aPtr, aWindow)
: mIsRemote(!aWindow)
, mWindow(aPtr, aWindow)
, mEditable(aEditableChild)
, mEditableAttached(!mIsRemote)
, mIMERanges(new TextRangeArray())
, mIMEMaskEventsCount(1) // Mask IME events since there's no focus yet
, mIMEUpdatingContext(false)
@ -167,12 +181,19 @@ public:
, mIMEMonitorCursor(false)
{}
// Constructor for content process GeckoEditableChild.
GeckoEditableSupport(java::GeckoEditableChild::Param aEditableChild)
: GeckoEditableSupport(nullptr, nullptr, aEditableChild)
{}
NS_DECL_ISUPPORTS
// TextEventDispatcherListener methods
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
const IMENotification& aNotification) override;
NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
NS_IMETHOD_(void) OnRemovedFrom(
TextEventDispatcher* aTextEventDispatcher) override;
@ -182,8 +203,6 @@ public:
uint32_t aIndexOfKeypress,
void* aData) override;
nsIMEUpdatePreference GetIMEUpdatePreference();
void SetInputContext(const InputContext& aContext,
const InputContextAction& aAction);
@ -194,8 +213,9 @@ public:
void OnDetach() {
RefPtr<GeckoEditableSupport> self(this);
nsAppShell::PostEvent([self] {
DisposeNative(self->mEditable);
nsAppShell::PostEvent([this, self] {
mEditableAttached = false;
DisposeNative(mEditable);
});
}

View File

@ -0,0 +1,62 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; 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/. */
#ifndef GeckoProcessManager_h
#define GeckoProcessManager_h
#include "GeneratedJNINatives.h"
#include "WidgetUtils.h"
#include "nsAppShell.h"
#include "nsWindow.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/ContentProcessManager.h"
namespace mozilla {
class GeckoProcessManager final
: public java::GeckoProcessManager::Natives<GeckoProcessManager>
{
GeckoProcessManager() = delete;
static already_AddRefed<nsIWidget>
GetWidget(int64_t aContentId, int64_t aTabId)
{
using namespace dom;
MOZ_ASSERT(NS_IsMainThread());
ContentProcessManager* const cpm =
ContentProcessManager::GetSingleton();
NS_ENSURE_TRUE(cpm, nullptr);
RefPtr<TabParent> tab = cpm->GetTopLevelTabParentByProcessAndTabId(
ContentParentId(aContentId), TabId(aTabId));
NS_ENSURE_TRUE(tab, nullptr);
nsCOMPtr<nsPIDOMWindowOuter> domWin = tab->GetParentWindowOuter();
NS_ENSURE_TRUE(domWin, nullptr);
return WidgetUtils::DOMWindowToWidget(domWin);
}
public:
static jni::Object::LocalRef
GetEditableParent(int64_t aContentId, int64_t aTabId)
{
// On binder thread.
jni::Object::GlobalRef ret;
nsAppShell::SyncRunEvent([aContentId, aTabId, &ret] {
nsCOMPtr<nsIWidget> widget = GetWidget(aContentId, aTabId);
if (widget) {
ret = static_cast<nsWindow*>(widget.get())->GetEditableParent();
}
});
return ret;
}
};
} // namespace mozilla
#endif // GeckoProcessManager_h

View File

@ -459,6 +459,21 @@ const JNINativeMethod VsyncSource::Natives<Impl>::methods[] = {
::template Wrap<&Impl::NotifyVsync>)
};
template<class Impl>
class GeckoProcessManager::Natives : public mozilla::jni::NativeImpl<GeckoProcessManager, Impl>
{
public:
static const JNINativeMethod methods[1];
};
template<class Impl>
const JNINativeMethod GeckoProcessManager::Natives<Impl>::methods[] = {
mozilla::jni::MakeNativeMethod<GeckoProcessManager::GetEditableParent_t>(
mozilla::jni::NativeStub<GeckoProcessManager::GetEditableParent_t, Impl>
::template Wrap<&Impl::GetEditableParent>)
};
} /* java */
} /* mozilla */
#endif // GeneratedJNINatives_h

View File

@ -859,6 +859,14 @@ auto GeckoThread::CheckAndSetState(mozilla::jni::Object::Param a0, mozilla::jni:
return mozilla::jni::Method<CheckAndSetState_t>::Call(GeckoThread::Context(), nullptr, a0, a1);
}
constexpr char GeckoThread::IsChildProcess_t::name[];
constexpr char GeckoThread::IsChildProcess_t::signature[];
auto GeckoThread::IsChildProcess() -> bool
{
return mozilla::jni::Method<IsChildProcess_t>::Call(GeckoThread::Context(), nullptr);
}
constexpr char GeckoThread::CreateServices_t::name[];
constexpr char GeckoThread::CreateServices_t::signature[];
@ -1750,6 +1758,23 @@ auto VsyncSource::INSTANCE() -> VsyncSource::LocalRef
return mozilla::jni::Field<INSTANCE_t>::Get(VsyncSource::Context(), nullptr);
}
const char GeckoProcessManager::name[] =
"org/mozilla/gecko/process/GeckoProcessManager";
constexpr char GeckoProcessManager::GetEditableParent_t::name[];
constexpr char GeckoProcessManager::GetEditableParent_t::signature[];
const char GeckoServiceChildProcess::name[] =
"org/mozilla/gecko/process/GeckoServiceChildProcess";
constexpr char GeckoServiceChildProcess::GetEditableParent_t::name[];
constexpr char GeckoServiceChildProcess::GetEditableParent_t::signature[];
auto GeckoServiceChildProcess::GetEditableParent(int64_t a0, int64_t a1) -> mozilla::jni::Object::LocalRef
{
return mozilla::jni::Method<GetEditableParent_t>::Call(GeckoServiceChildProcess::Context(), nullptr, a0, a1);
}
const char Clipboard::name[] =
"org/mozilla/gecko/util/Clipboard";

View File

@ -2460,6 +2460,8 @@ public:
static const int32_t NOTIFY_IME_OF_FOCUS = 1;
static const int32_t NOTIFY_IME_OF_TOKEN = -3;
static const int32_t NOTIFY_IME_OPEN_VKB = -2;
static const int32_t NOTIFY_IME_REPLY_EVENT = -1;
@ -2587,6 +2589,25 @@ public:
static auto CheckAndSetState(mozilla::jni::Object::Param, mozilla::jni::Object::Param) -> bool;
struct IsChildProcess_t {
typedef GeckoThread Owner;
typedef bool ReturnType;
typedef bool SetterType;
typedef mozilla::jni::Args<> Args;
static constexpr char name[] = "isChildProcess";
static constexpr char signature[] =
"()Z";
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 IsChildProcess() -> bool;
struct CreateServices_t {
typedef GeckoThread Owner;
typedef void ReturnType;
@ -5003,6 +5024,71 @@ public:
template<class Impl> class Natives;
};
class GeckoProcessManager : public mozilla::jni::ObjectBase<GeckoProcessManager>
{
public:
static const char name[];
explicit GeckoProcessManager(const Context& ctx) : ObjectBase<GeckoProcessManager>(ctx) {}
struct GetEditableParent_t {
typedef GeckoProcessManager Owner;
typedef mozilla::jni::Object::LocalRef ReturnType;
typedef mozilla::jni::Object::Param SetterType;
typedef mozilla::jni::Args<
int64_t,
int64_t> Args;
static constexpr char name[] = "nativeGetEditableParent";
static constexpr char signature[] =
"(JJ)Lorg/mozilla/gecko/IGeckoEditableParent;";
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 const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
template<class Impl> class Natives;
};
class GeckoServiceChildProcess : public mozilla::jni::ObjectBase<GeckoServiceChildProcess>
{
public:
static const char name[];
explicit GeckoServiceChildProcess(const Context& ctx) : ObjectBase<GeckoServiceChildProcess>(ctx) {}
struct GetEditableParent_t {
typedef GeckoServiceChildProcess Owner;
typedef mozilla::jni::Object::LocalRef ReturnType;
typedef mozilla::jni::Object::Param SetterType;
typedef mozilla::jni::Args<
int64_t,
int64_t> Args;
static constexpr char name[] = "getEditableParent";
static constexpr char signature[] =
"(JJ)Lorg/mozilla/gecko/IGeckoEditableParent;";
static const bool isStatic = true;
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;
};
static auto GetEditableParent(int64_t, int64_t) -> mozilla::jni::Object::LocalRef;
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::GECKO;
};
class Clipboard : public mozilla::jni::ObjectBase<Clipboard>
{
public:

View File

@ -124,6 +124,12 @@ void SetGeckoThreadEnv(JNIEnv* aEnv)
"loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
MOZ_ASSERT(sClassLoader && sClassLoaderLoadClass);
if (java::GeckoThread::IsChildProcess()) {
// Disallow Fennec-only classes from being used in child processes.
sIsFennec = false;
return;
}
auto geckoAppClass = Class::LocalRef::Adopt(
aEnv->FindClass("org/mozilla/gecko/GeckoApp"));
aEnv->ExceptionClear();

View File

@ -24,6 +24,7 @@
#include "nsIDOMWakeLockListener.h"
#include "nsIPowerManagerService.h"
#include "nsISpeculativeConnect.h"
#include "nsITabChild.h"
#include "nsIURIFixup.h"
#include "nsCategoryManagerUtils.h"
#include "nsCDefaultURIFixup.h"
@ -35,6 +36,7 @@
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "mozilla/Hal.h"
#include "mozilla/dom/TabChild.h"
#include "prenv.h"
#include "AndroidBridge.h"
@ -65,6 +67,7 @@
#include "ANRReporter.h"
#include "GeckoBatteryManager.h"
#include "GeckoNetworkManager.h"
#include "GeckoProcessManager.h"
#include "GeckoScreenOrientation.h"
#include "PrefsHelper.h"
#include "fennec/MemoryMonitor.h"
@ -384,6 +387,10 @@ nsAppShell::nsAppShell()
}
if (!XRE_IsParentProcess()) {
if (jni::IsAvailable()) {
// Set the corresponding state in GeckoThread.
java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
}
return;
}
@ -394,6 +401,7 @@ nsAppShell::nsAppShell()
GeckoThreadSupport::Init();
mozilla::GeckoBatteryManager::Init();
mozilla::GeckoNetworkManager::Init();
mozilla::GeckoProcessManager::Init();
mozilla::GeckoScreenOrientation::Init();
mozilla::PrefsHelper::Init();
nsWindow::InitNatives();
@ -437,7 +445,7 @@ nsAppShell::~nsAppShell()
sWakeLockListener = nullptr;
}
if (jni::IsAvailable()) {
if (jni::IsAvailable() && XRE_IsParentProcess()) {
DestroyAndroidUiThread();
AndroidBridge::DeconstructBridge();
}
@ -502,6 +510,7 @@ nsAppShell::Init()
obsServ->AddObserver(this, "browser-delayed-startup-finished", false);
obsServ->AddObserver(this, "profile-after-change", false);
obsServ->AddObserver(this, "chrome-document-loaded", false);
obsServ->AddObserver(this, "tab-child-created", false);
obsServ->AddObserver(this, "quit-application-granted", false);
obsServ->AddObserver(this, "xpcom-shutdown", false);
}
@ -595,6 +604,37 @@ nsAppShell::Observe(nsISupports* aSubject,
if (jni::IsAvailable()) {
mozilla::PrefsHelper::OnPrefChange(aData);
}
} else if (!strcmp(aTopic, "tab-child-created")) {
// Associate the PuppetWidget of the newly-created TabChild with a
// GeckoEditableChild instance.
MOZ_ASSERT(!XRE_IsParentProcess());
dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
nsCOMPtr<nsITabChild> ptabChild = do_QueryInterface(aSubject);
NS_ENSURE_TRUE(contentChild && ptabChild, NS_OK);
// Get the content/tab ID in order to get the correct
// IGeckoEditableParent object, which GeckoEditableChild uses to
// communicate with the parent process.
const auto tabChild = static_cast<dom::TabChild*>(ptabChild.get());
const uint64_t contentId = contentChild->GetID();
const uint64_t tabId = tabChild->GetTabId();
NS_ENSURE_TRUE(contentId && tabId, NS_OK);
auto editableParent = java::GeckoServiceChildProcess::GetEditableParent(
contentId, tabId);
NS_ENSURE_TRUE(editableParent, NS_OK);
RefPtr<widget::PuppetWidget> widget(tabChild->WebWidget());
auto editableChild = java::GeckoEditableChild::New(editableParent);
NS_ENSURE_TRUE(widget && editableChild, NS_OK);
RefPtr<GeckoEditableSupport> editableSupport =
new GeckoEditableSupport(editableChild);
// Tell PuppetWidget to use our listener for IME operations.
widget->SetNativeTextEventDispatcherListener(editableSupport);
}
if (removeObserver) {
@ -626,7 +666,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
// (bug 750713). Looper messages effectively have the lowest
// priority because we only process them before we're about to
// wait for new events.
if (jni::IsAvailable() &&
if (jni::IsAvailable() && XRE_IsParentProcess() &&
AndroidBridge::Bridge()->PumpMessageLoop()) {
return true;
}

View File

@ -13,6 +13,7 @@
#include "mozilla/Monitor.h"
#include "mozilla/Move.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/jni/Natives.h"
@ -147,6 +148,13 @@ public:
mozilla::UniquePtr<Event>(*eventFactory)(
mozilla::UniquePtr<Event>&&) = nullptr);
template<typename T> static
typename mozilla::EnableIf<!mozilla::IsBaseOf<Event, T>::value, void>::Type
SyncRunEvent(T&& lambda)
{
SyncRunEvent(LambdaEvent<T>(mozilla::Forward<T>(lambda)));
}
static already_AddRefed<nsIURI> ResolveURI(const nsCString& aUriStr);
void SetBrowserApp(nsIAndroidBrowserApp* aBrowserApp) {

View File

@ -2121,20 +2121,6 @@ nsWindow::GetInputContext()
return top->mEditableSupport->GetInputContext();
}
nsIMEUpdatePreference
nsWindow::GetIMEUpdatePreference()
{
nsWindow* top = FindTopLevel();
MOZ_ASSERT(top);
if (!top->mEditableSupport) {
// Non-GeckoView windows don't support IME operations.
return nsIMEUpdatePreference();
}
return top->mEditableSupport->GetIMEUpdatePreference();
}
nsresult
nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
TouchPointerState aPointerState,

View File

@ -130,7 +130,9 @@ public:
, mWindowLock(NativePtr<Impl>::sName)
{
MOZ_ASSERT(NS_IsMainThread());
mPtr->mPtr = this;
if (mPtr) {
mPtr->mPtr = this;
}
}
~WindowPtr()
@ -272,7 +274,6 @@ public:
virtual void SetInputContext(const InputContext& aContext,
const InputContextAction& aAction) override;
virtual InputContext GetInputContext() override;
virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
void SetSelectionDragState(bool aState);
LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
@ -315,6 +316,8 @@ public:
// event (like a keypress or mouse click).
void UserActivity();
mozilla::java::GeckoEditable::Ref& GetEditableParent() { return mEditable; }
protected:
void BringToFront();
nsWindow *FindTopLevel();

View File

@ -774,6 +774,7 @@ public:
// TextEventDispatcherListener methods
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
const IMENotification& aNotification) override;
NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
NS_IMETHOD_(void) OnRemovedFrom(
TextEventDispatcher* aTextEventDispatcher) override;
NS_IMETHOD_(void) WillDispatchKeyboardEvent(

View File

@ -2627,6 +2627,15 @@ IMEInputHandler::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
}
}
NS_IMETHODIMP_(nsIMEUpdatePreference)
IMEInputHandler::GetIMEUpdatePreference()
{
// XXX Shouldn't we move floating window which shows composition string
// when plugin has focus and its parent is scrolled or the window is
// moved?
return nsIMEUpdatePreference();
}
NS_IMETHODIMP_(void)
IMEInputHandler::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
{

View File

@ -398,7 +398,6 @@ public:
void* aCallbackData,
uint32_t aGeckoKeyCode,
uint32_t aCocoaKeyCode);
virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
virtual nsTransparencyMode GetTransparencyMode() override;
virtual void SetTransparencyMode(nsTransparencyMode aMode) override;

View File

@ -1893,15 +1893,6 @@ nsChildView::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
return keyBindings->Execute(aEvent, aCallback, aCallbackData);
}
nsIMEUpdatePreference
nsChildView::GetIMEUpdatePreference()
{
// XXX Shouldn't we move floating window which shows composition string
// when plugin has focus and its parent is scrolled or the window is
// moved?
return nsIMEUpdatePreference();
}
NSView<mozView>* nsChildView::GetEditorView()
{
NSView<mozView>* editorView = mView;

View File

@ -324,8 +324,8 @@ IMContextWrapper::GetTextEventDispatcher()
return dispatcher;
}
nsIMEUpdatePreference
IMContextWrapper::GetIMEUpdatePreference() const
NS_IMETHODIMP_(nsIMEUpdatePreference)
IMContextWrapper::GetIMEUpdatePreference()
{
// While a plugin has focus, IMContextWrapper doesn't need any
// notifications.

View File

@ -33,6 +33,7 @@ public:
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
const IMENotification& aNotification) override;
NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
NS_IMETHOD_(void) OnRemovedFrom(
TextEventDispatcher* aTextEventDispatcher) override;
NS_IMETHOD_(void) WillDispatchKeyboardEvent(
@ -51,8 +52,6 @@ public:
// I.e., the focus is in the normal editors.
bool IsEnabled() const;
nsIMEUpdatePreference GetIMEUpdatePreference() const;
// OnFocusWindow is a notification that aWindow is going to be focused.
void OnFocusWindow(nsWindow* aWindow);
// OnBlurWindow is a notification that aWindow is going to be unfocused.

View File

@ -6099,15 +6099,6 @@ nsWindow::GetInputContext()
return context;
}
nsIMEUpdatePreference
nsWindow::GetIMEUpdatePreference()
{
if (!mIMContext) {
return nsIMEUpdatePreference();
}
return mIMContext->GetIMEUpdatePreference();
}
TextEventDispatcherListener*
nsWindow::GetNativeTextEventDispatcherListener()
{

View File

@ -274,7 +274,6 @@ public:
virtual void SetInputContext(const InputContext& aContext,
const InputContextAction& aAction) override;
virtual InputContext GetInputContext() override;
virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
virtual TextEventDispatcherListener*
GetNativeTextEventDispatcherListener() override;
bool ExecuteNativeKeyBindingRemapped(

View File

@ -1813,6 +1813,18 @@ nsBaseWidget::NotifyIME(const IMENotification& aIMENotification)
}
}
nsIMEUpdatePreference
nsBaseWidget::GetIMEUpdatePreference()
{
RefPtr<TextEventDispatcherListener> listener =
GetNativeTextEventDispatcherListener();
if (!listener) {
// Default is to not send additional change notifications to NotifyIME.
return nsIMEUpdatePreference();
}
return listener->GetIMEUpdatePreference();
}
void
nsBaseWidget::EnsureTextEventDispatcher()
{

View File

@ -288,7 +288,7 @@ public:
void* aCallbackData) override { return false; }
bool ComputeShouldAccelerate();
virtual bool WidgetTypeSupportsAcceleration() { return true; }
virtual nsIMEUpdatePreference GetIMEUpdatePreference() override { return nsIMEUpdatePreference(); }
virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
virtual MOZ_MUST_USE nsresult OnDefaultButtonLoaded(const LayoutDeviceIntRect& aButtonRect) override { return NS_ERROR_NOT_IMPLEMENTED; }
virtual already_AddRefed<nsIWidget>
CreateChild(const LayoutDeviceIntRect& aRect,

View File

@ -56,6 +56,12 @@ WinTextEventDispatcherListener::NotifyIME(
return IMEHandler::NotifyIME(window, aNotification);
}
NS_IMETHODIMP_(nsIMEUpdatePreference)
WinTextEventDispatcherListener::GetIMEUpdatePreference()
{
return IMEHandler::GetUpdatePreference();
}
NS_IMETHODIMP_(void)
WinTextEventDispatcherListener::OnRemovedFrom(
TextEventDispatcher* aTextEventDispatcher)

View File

@ -29,6 +29,7 @@ public:
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
const IMENotification& aNotification) override;
NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
NS_IMETHOD_(void) OnRemovedFrom(
TextEventDispatcher* aTextEventDispatcher) override;
NS_IMETHOD_(void) WillDispatchKeyboardEvent(

View File

@ -7225,12 +7225,6 @@ nsWindow::GetInputContext()
return mInputContext;
}
nsIMEUpdatePreference
nsWindow::GetIMEUpdatePreference()
{
return IMEHandler::GetUpdatePreference();
}
TextEventDispatcherListener*
nsWindow::GetNativeTextEventDispatcherListener()
{

View File

@ -210,7 +210,6 @@ public:
virtual nsTransparencyMode GetTransparencyMode() override;
virtual void UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) override;
#endif // MOZ_XUL
virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
virtual nsresult SetNonClientMargins(LayoutDeviceIntMargin& aMargins) override;
void SetDrawsInTitlebar(bool aState) override;
virtual void UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) override;

View File

@ -22,6 +22,10 @@
#include "XREChildData.h"
#include "XREShellData.h"
#if defined(MOZ_WIDGET_ANDROID)
#include <jni.h>
#endif
/**
* A directory service key which provides the platform-correct "application
* data" directory as follows, where $name and $vendor are as defined above and
@ -407,7 +411,7 @@ XRE_API(const char*,
#if defined(MOZ_WIDGET_ANDROID)
XRE_API(void,
XRE_SetAndroidChildFds, (int crashFd, int ipcFd))
XRE_SetAndroidChildFds, (JNIEnv* env, int crashFd, int ipcFd))
#endif // defined(MOZ_WIDGET_ANDROID)
XRE_API(void,