зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1755094 - Adjust SelectionActionDelegate for ActorDispatcher. r=owlish
ActorDispatcher does not support receiving multiple callbacks for one message so we need to change the strategy a little bit. We need to centralize handling actions are each sequential ID is incremented independently for each child, so it won't work for iframes. To avoid this problem in the future, the sequential ID is no just a randomly-generated UUID. We also do all the checking on the parent process, so we don't need to check in the child again whether we got a message that was out-of-date. Differential Revision: https://phabricator.services.mozilla.com/D141645
This commit is contained in:
Родитель
fe75b477df
Коммит
07fb9f95ee
|
@ -21,7 +21,7 @@ class SelectionActionDelegateChild extends GeckoViewActorChild {
|
|||
constructor(aModuleName, aMessageManager) {
|
||||
super(aModuleName, aMessageManager);
|
||||
|
||||
this._seqNo = 0;
|
||||
this._actionCallback = () => {};
|
||||
this._isActive = false;
|
||||
this._previousMessage = "";
|
||||
|
||||
|
@ -133,6 +133,16 @@ class SelectionActionDelegateChild extends GeckoViewActorChild {
|
|||
},
|
||||
];
|
||||
|
||||
receiveMessage({ name, data }) {
|
||||
debug`receiveMessage ${name}`;
|
||||
|
||||
switch (name) {
|
||||
case "ExecuteSelectionAction": {
|
||||
this._actionCallback(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_performPaste() {
|
||||
this.handleEvent({ type: "pagehide" });
|
||||
this.docShell.doCommand("cmd_paste");
|
||||
|
@ -307,8 +317,6 @@ class SelectionActionDelegateChild extends GeckoViewActorChild {
|
|||
const password = this._isPasswordField(aEvent);
|
||||
|
||||
const msg = {
|
||||
type: "GeckoView:ShowSelectionAction",
|
||||
seqNo: this._seqNo,
|
||||
collapsed: aEvent.collapsed,
|
||||
editable: aEvent.selectionEditable,
|
||||
password,
|
||||
|
@ -333,29 +341,21 @@ class SelectionActionDelegateChild extends GeckoViewActorChild {
|
|||
return;
|
||||
}
|
||||
|
||||
msg.seqNo = ++this._seqNo;
|
||||
this._isActive = true;
|
||||
this._previousMessage = JSON.stringify(msg);
|
||||
|
||||
this.eventDispatcher.sendRequest(msg, {
|
||||
onSuccess: response => {
|
||||
if (response.seqNo !== this._seqNo) {
|
||||
// Stale action.
|
||||
warn`Stale response ${response.id}`;
|
||||
return;
|
||||
}
|
||||
const action = actions.find(action => action.id === response.id);
|
||||
if (action) {
|
||||
debug`Performing ${response.id}`;
|
||||
action.perform.call(this, aEvent, response);
|
||||
} else {
|
||||
warn`Invalid action ${response.id}`;
|
||||
}
|
||||
},
|
||||
onError: _ => {
|
||||
// Do nothing; we can get here if the delegate was just unregistered.
|
||||
},
|
||||
});
|
||||
// We can't just listen to the response of the message because we accept
|
||||
// multiple callbacks.
|
||||
this._actionCallback = data => {
|
||||
const action = actions.find(action => action.id === data.id);
|
||||
if (action) {
|
||||
debug`Performing ${data.id}`;
|
||||
action.perform.call(this, aEvent);
|
||||
} else {
|
||||
warn`Invalid action ${data.id}`;
|
||||
}
|
||||
};
|
||||
this.sendAsyncMessage("ShowSelectionAction", msg);
|
||||
} else if (
|
||||
[
|
||||
"invisibleselection",
|
||||
|
@ -367,7 +367,6 @@ class SelectionActionDelegateChild extends GeckoViewActorChild {
|
|||
if (!this._isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isActive = false;
|
||||
|
||||
// Mark previous actions as stale. Don't do this for "invisibleselection"
|
||||
|
@ -377,10 +376,7 @@ class SelectionActionDelegateChild extends GeckoViewActorChild {
|
|||
this._seqNo++;
|
||||
}
|
||||
|
||||
this.eventDispatcher.sendRequest({
|
||||
type: "GeckoView:HideSelectionAction",
|
||||
reason,
|
||||
});
|
||||
this.sendAsyncMessage("HideSelectionAction", { reason });
|
||||
} else if (reason == "dragcaret") {
|
||||
// nothing for selection action
|
||||
} else {
|
||||
|
|
|
@ -8,6 +8,71 @@ var EXPORTED_SYMBOLS = ["SelectionActionDelegateParent"];
|
|||
const { GeckoViewActorParent } = ChromeUtils.import(
|
||||
"resource://gre/modules/GeckoViewActorParent.jsm"
|
||||
);
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// For this.eventDispatcher in the child
|
||||
class SelectionActionDelegateParent extends GeckoViewActorParent {}
|
||||
class SelectionActionDelegateParent extends GeckoViewActorParent {
|
||||
respondTo = null;
|
||||
actionId = null;
|
||||
|
||||
get rootActor() {
|
||||
return this.browsingContext.top.currentWindowGlobal.getActor(
|
||||
"SelectionActionDelegate"
|
||||
);
|
||||
}
|
||||
|
||||
receiveMessage(aMessage) {
|
||||
const { data, name } = aMessage;
|
||||
switch (name) {
|
||||
case "ShowSelectionAction": {
|
||||
this.rootActor.showSelectionAction(this, data);
|
||||
break;
|
||||
}
|
||||
|
||||
case "HideSelectionAction": {
|
||||
this.rootActor.hideSelectionAction(this, data.reason);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
super.receiveMessage(aMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideSelectionAction(aRespondTo, reason) {
|
||||
// Mark previous actions as stale. Don't do this for "invisibleselection"
|
||||
// or "scroll" because previous actions should still be valid even after
|
||||
// these events occur.
|
||||
if (reason !== "invisibleselection" && reason !== "scroll") {
|
||||
this.actionId = null;
|
||||
}
|
||||
|
||||
this.eventDispatcher?.sendRequest({
|
||||
type: "GeckoView:HideSelectionAction",
|
||||
reason,
|
||||
});
|
||||
}
|
||||
|
||||
showSelectionAction(aRespondTo, aData) {
|
||||
this.actionId = Services.uuid.generateUUID().toString();
|
||||
this.respondTo = aRespondTo;
|
||||
|
||||
this.eventDispatcher?.sendRequest({
|
||||
type: "GeckoView:ShowSelectionAction",
|
||||
actionId: this.actionId,
|
||||
...aData,
|
||||
});
|
||||
}
|
||||
|
||||
executeSelectionAction(aData) {
|
||||
if (this.actionId === null || aData.actionId != this.actionId) {
|
||||
warn`Stale response ${aData.id} ${aData.actionId}`;
|
||||
return;
|
||||
}
|
||||
this.respondTo.sendAsyncMessage("ExecuteSelectionAction", aData);
|
||||
}
|
||||
}
|
||||
|
||||
const { debug, warn } = SelectionActionDelegateParent.initLogging(
|
||||
"SelectionActionDelegate"
|
||||
);
|
||||
|
|
|
@ -635,6 +635,7 @@ function startup() {
|
|||
{
|
||||
name: "GeckoViewSelectionAction",
|
||||
onEnable: {
|
||||
resource: "resource://gre/modules/GeckoViewSelectionAction.jsm",
|
||||
actors: {
|
||||
SelectionActionDelegate: {
|
||||
parent: {
|
||||
|
|
|
@ -900,7 +900,7 @@ public class GeckoSession {
|
|||
final @SelectionActionDelegateAction HashSet<String> actionsSet =
|
||||
new HashSet<>(Arrays.asList(message.getStringArray("actions")));
|
||||
final SelectionActionDelegate.Selection selection =
|
||||
new SelectionActionDelegate.Selection(message, actionsSet, callback);
|
||||
new SelectionActionDelegate.Selection(message, actionsSet, mEventDispatcher);
|
||||
|
||||
delegate.onShowActionRequest(GeckoSession.this, selection);
|
||||
|
||||
|
@ -3406,14 +3406,14 @@ public class GeckoSession {
|
|||
/** Set of valid actions available through {@link Selection#execute(String)} */
|
||||
public final @NonNull @SelectionActionDelegateAction Collection<String> availableActions;
|
||||
|
||||
private final int mSeqNo;
|
||||
private final String mActionId;
|
||||
|
||||
private final EventCallback mEventCallback;
|
||||
private final WeakReference<EventDispatcher> mEventDispatcher;
|
||||
|
||||
/* package */ Selection(
|
||||
final GeckoBundle bundle,
|
||||
final @NonNull @SelectionActionDelegateAction Set<String> actions,
|
||||
final EventCallback callback) {
|
||||
final EventDispatcher eventDispatcher) {
|
||||
flags =
|
||||
(bundle.getBoolean("collapsed") ? SelectionActionDelegate.FLAG_IS_COLLAPSED : 0)
|
||||
| (bundle.getBoolean("editable") ? SelectionActionDelegate.FLAG_IS_EDITABLE : 0)
|
||||
|
@ -3421,8 +3421,8 @@ public class GeckoSession {
|
|||
text = bundle.getString("selection");
|
||||
clientRect = bundle.getRectF("clientRect");
|
||||
availableActions = actions;
|
||||
mSeqNo = bundle.getInt("seqNo");
|
||||
mEventCallback = callback;
|
||||
mActionId = bundle.getString("actionId");
|
||||
mEventDispatcher = new WeakReference<>(eventDispatcher);
|
||||
}
|
||||
|
||||
/** Empty constructor for tests. */
|
||||
|
@ -3431,8 +3431,8 @@ public class GeckoSession {
|
|||
text = "";
|
||||
clientRect = null;
|
||||
availableActions = new HashSet<>();
|
||||
mSeqNo = 0;
|
||||
mEventCallback = null;
|
||||
mActionId = null;
|
||||
mEventDispatcher = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3458,10 +3458,16 @@ public class GeckoSession {
|
|||
if (!isActionAvailable(action)) {
|
||||
throw new IllegalStateException("Action not available");
|
||||
}
|
||||
final EventDispatcher eventDispatcher = mEventDispatcher.get();
|
||||
if (eventDispatcher == null) {
|
||||
// The session is not available anymore, nothing really to do
|
||||
Log.w(LOGTAG, "Calling execute on a stale Selection.");
|
||||
return;
|
||||
}
|
||||
final GeckoBundle response = new GeckoBundle(2);
|
||||
response.putString("id", action);
|
||||
response.putInt("seqNo", mSeqNo);
|
||||
mEventCallback.sendSuccess(response);
|
||||
response.putString("actionId", mActionId);
|
||||
eventDispatcher.dispatch("GeckoView:ExecuteSelectionAction", response);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["GeckoViewSelectionAction"];
|
||||
|
||||
const { GeckoViewModule } = ChromeUtils.import(
|
||||
"resource://gre/modules/GeckoViewModule.jsm"
|
||||
);
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
});
|
||||
|
||||
class GeckoViewSelectionAction extends GeckoViewModule {
|
||||
onEnable() {
|
||||
debug`onEnable`;
|
||||
this.registerListener(["GeckoView:ExecuteSelectionAction"]);
|
||||
}
|
||||
|
||||
onDisable() {
|
||||
debug`onDisable`;
|
||||
this.unregisterListener();
|
||||
}
|
||||
|
||||
get actor() {
|
||||
return this.getActor("SelectionActionDelegate");
|
||||
}
|
||||
|
||||
// Bundle event handler.
|
||||
onEvent(aEvent, aData, aCallback) {
|
||||
debug`onEvent: ${aEvent}`;
|
||||
|
||||
switch (aEvent) {
|
||||
case "GeckoView:ExecuteSelectionAction": {
|
||||
this.actor.executeSelectionAction(aData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { debug, warn } = GeckoViewSelectionAction.initLogging(
|
||||
"GeckoViewSelectionAction"
|
||||
);
|
|
@ -26,6 +26,7 @@ EXTRA_JS_MODULES += [
|
|||
"GeckoViewProgress.jsm",
|
||||
"GeckoViewPushController.jsm",
|
||||
"GeckoViewRemoteDebugger.jsm",
|
||||
"GeckoViewSelectionAction.jsm",
|
||||
"GeckoViewSettings.jsm",
|
||||
"GeckoViewStorageController.jsm",
|
||||
"GeckoViewTab.jsm",
|
||||
|
|
Загрузка…
Ссылка в новой задаче