mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1134769, move selection info retrieval on context menu to content process, r=mconley
This commit is contained in:
parent
531d28100a
commit
e662ba16db
@ -5278,51 +5278,17 @@ function UpdateDynamicShortcutTooltipText(aTooltip) {
|
||||
aTooltip.setAttribute("label", gDynamicTooltipCache.get(nodeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selected text in the active browser. Leading and trailing
|
||||
* whitespace is removed, and consecutive whitespace is replaced by a single
|
||||
* space. A maximum of 150 characters will be returned, regardless of the value
|
||||
* of aCharLen.
|
||||
*
|
||||
* @param aCharLen
|
||||
* The maximum number of characters to return.
|
||||
*/
|
||||
function getBrowserSelection(aCharLen) {
|
||||
// selections of more than 150 characters aren't useful
|
||||
const kMaxSelectionLen = 150;
|
||||
const charLen = Math.min(aCharLen || kMaxSelectionLen, kMaxSelectionLen);
|
||||
Deprecated.warning("getBrowserSelection",
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=1134769");
|
||||
|
||||
let [element, focusedWindow] = BrowserUtils.getFocusSync(document);
|
||||
var selection = focusedWindow.getSelection().toString();
|
||||
// try getting a selected text in text input.
|
||||
if (!selection) {
|
||||
var isOnTextInput = function isOnTextInput(elem) {
|
||||
// we avoid to return a value if a selection is in password field.
|
||||
// ref. bug 565717
|
||||
return elem instanceof HTMLTextAreaElement ||
|
||||
(elem instanceof HTMLInputElement && elem.mozIsTextField(true));
|
||||
};
|
||||
|
||||
if (isOnTextInput(element)) {
|
||||
selection = element.QueryInterface(Ci.nsIDOMNSEditableElement)
|
||||
.editor.selection.toString();
|
||||
}
|
||||
let focusedElement = document.activeElement;
|
||||
if (focusedElement && focusedElement.localName == "browser" &&
|
||||
focusedElement.isRemoteBrowser) {
|
||||
throw "getBrowserSelection doesn't support child process windows";
|
||||
}
|
||||
|
||||
if (selection) {
|
||||
if (selection.length > charLen) {
|
||||
// only use the first charLen important chars. see bug 221361
|
||||
var pattern = new RegExp("^(?:\\s*.){0," + charLen + "}");
|
||||
pattern.test(selection);
|
||||
selection = RegExp.lastMatch;
|
||||
}
|
||||
|
||||
selection = selection.trim().replace(/\s+/g, " ");
|
||||
|
||||
if (selection.length > charLen)
|
||||
selection = selection.substr(0, charLen);
|
||||
}
|
||||
return selection;
|
||||
return BrowserUtils.getSelectionDetails(window, aCharLen).text;
|
||||
}
|
||||
|
||||
var gWebPanelURI;
|
||||
|
@ -114,6 +114,8 @@ let handleContentContextMenu = function (event) {
|
||||
}
|
||||
}
|
||||
|
||||
let selectionInfo = BrowserUtils.getSelectionDetails(content);
|
||||
|
||||
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
let editFlags = SpellCheckHelper.isEditable(event.target, content);
|
||||
let spellInfo;
|
||||
@ -136,7 +138,7 @@ let handleContentContextMenu = function (event) {
|
||||
{ editFlags, spellInfo, customMenuItems, addonInfo,
|
||||
principal, docLocation, charSet, baseURI, referrer,
|
||||
referrerPolicy, contentType, contentDisposition,
|
||||
frameOuterWindowID },
|
||||
frameOuterWindowID, selectionInfo },
|
||||
{ event, popupNode: event.target });
|
||||
}
|
||||
else {
|
||||
@ -156,6 +158,7 @@ let handleContentContextMenu = function (event) {
|
||||
referrerPolicy: referrerPolicy,
|
||||
contentType: contentType,
|
||||
contentDisposition: contentDisposition,
|
||||
selectionInfo: selectionInfo,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ nsContextMenu.prototype = {
|
||||
Ci.nsIPrefLocalizedString).data;
|
||||
} catch (e) { }
|
||||
|
||||
this.isContentSelected = this.isContentSelection();
|
||||
this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed;
|
||||
this.onPlainTextLink = false;
|
||||
|
||||
// Initialize (disable/remove) menu items.
|
||||
@ -93,67 +93,15 @@ nsContextMenu.prototype = {
|
||||
(mailtoHandler.preferredApplicationHandler instanceof Ci.nsIWebHandlerApp));
|
||||
}
|
||||
|
||||
// Time to do some bad things and see if we've highlighted a URL that
|
||||
// isn't actually linked.
|
||||
if (this.isTextSelected && !this.onLink) {
|
||||
// Ok, we have some text, let's figure out if it looks like a URL.
|
||||
let selection = this.focusedWindow.getSelection();
|
||||
let linkText = selection.toString().trim();
|
||||
let uri;
|
||||
if (/^(?:https?|ftp):/i.test(linkText)) {
|
||||
try {
|
||||
uri = makeURI(linkText);
|
||||
} catch (ex) {}
|
||||
}
|
||||
// Check if this could be a valid url, just missing the protocol.
|
||||
else if (/^(?:[a-z\d-]+\.)+[a-z]+$/i.test(linkText)) {
|
||||
// Now let's see if this is an intentional link selection. Our guess is
|
||||
// based on whether the selection begins/ends with whitespace or is
|
||||
// preceded/followed by a non-word character.
|
||||
if (this.isTextSelected && !this.onLink &&
|
||||
this.selectionInfo && this.selectionInfo.linkURL) {
|
||||
this.linkURL = this.selectionInfo.linkURL;
|
||||
try {
|
||||
this.linkURI = makeURI(this.linkURL);
|
||||
} catch (ex) {}
|
||||
|
||||
// selection.toString() trims trailing whitespace, so we look for
|
||||
// that explicitly in the first and last ranges.
|
||||
let beginRange = selection.getRangeAt(0);
|
||||
let delimitedAtStart = /^\s/.test(beginRange);
|
||||
if (!delimitedAtStart) {
|
||||
let container = beginRange.startContainer;
|
||||
let offset = beginRange.startOffset;
|
||||
if (container.nodeType == Node.TEXT_NODE && offset > 0)
|
||||
delimitedAtStart = /\W/.test(container.textContent[offset - 1]);
|
||||
else
|
||||
delimitedAtStart = true;
|
||||
}
|
||||
|
||||
let delimitedAtEnd = false;
|
||||
if (delimitedAtStart) {
|
||||
let endRange = selection.getRangeAt(selection.rangeCount - 1);
|
||||
delimitedAtEnd = /\s$/.test(endRange);
|
||||
if (!delimitedAtEnd) {
|
||||
let container = endRange.endContainer;
|
||||
let offset = endRange.endOffset;
|
||||
if (container.nodeType == Node.TEXT_NODE &&
|
||||
offset < container.textContent.length)
|
||||
delimitedAtEnd = /\W/.test(container.textContent[offset]);
|
||||
else
|
||||
delimitedAtEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (delimitedAtStart && delimitedAtEnd) {
|
||||
let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"]
|
||||
.getService(Ci.nsIURIFixup);
|
||||
try {
|
||||
uri = uriFixup.createFixupURI(linkText, uriFixup.FIXUP_FLAG_NONE);
|
||||
} catch (ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (uri && uri.host) {
|
||||
this.linkURI = uri;
|
||||
this.linkURL = this.linkURI.spec;
|
||||
this.linkText = linkText;
|
||||
this.onPlainTextLink = true;
|
||||
}
|
||||
this.linkText = this.selectionInfo.linkText;
|
||||
this.onPlainTextLink = true;
|
||||
}
|
||||
|
||||
var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
|
||||
@ -590,16 +538,19 @@ nsContextMenu.prototype = {
|
||||
this.isDesignMode = false;
|
||||
this.onCTPPlugin = false;
|
||||
this.canSpellCheck = false;
|
||||
this.textSelected = getBrowserSelection();
|
||||
|
||||
if (this.isRemote) {
|
||||
this.selectionInfo = gContextMenuContentData.selectionInfo;
|
||||
} else {
|
||||
this.selectionInfo = BrowserUtils.getSelectionDetails(window);
|
||||
}
|
||||
|
||||
this.textSelected = this.selectionInfo.text;
|
||||
this.isTextSelected = this.textSelected.length != 0;
|
||||
|
||||
// Remember the node that was clicked.
|
||||
this.target = aNode;
|
||||
|
||||
let [elt, win] = BrowserUtils.getFocusSync(document);
|
||||
this.focusedWindow = win;
|
||||
this.focusedElement = elt;
|
||||
|
||||
let ownerDoc = this.target.ownerDocument;
|
||||
this.ownerDoc = ownerDoc;
|
||||
|
||||
@ -1545,11 +1496,6 @@ nsContextMenu.prototype = {
|
||||
return text;
|
||||
},
|
||||
|
||||
// Returns true if anything is selected.
|
||||
isContentSelection: function() {
|
||||
return !this.focusedWindow.getSelection().isCollapsed;
|
||||
},
|
||||
|
||||
isMediaURLReusable: function(aURL) {
|
||||
return !/^(?:blob|mediasource):/.test(aURL);
|
||||
},
|
||||
|
@ -3763,6 +3763,7 @@
|
||||
contentType: aMessage.data.contentType,
|
||||
contentDisposition: aMessage.data.contentDisposition,
|
||||
frameOuterWindowID: aMessage.data.frameOuterWindowID,
|
||||
selectionInfo: aMessage.data.selectionInfo,
|
||||
};
|
||||
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
|
||||
let event = gContextMenuContentData.event;
|
||||
|
@ -21,22 +21,6 @@ if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
"nsICrashReporter");
|
||||
}
|
||||
|
||||
let FocusSyncHandler = {
|
||||
init: function() {
|
||||
sendAsyncMessage("SetSyncHandler", {}, {handler: this});
|
||||
},
|
||||
|
||||
getFocusedElementAndWindow: function() {
|
||||
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||
|
||||
let focusedWindow = {};
|
||||
let elt = fm.getFocusedElementForWindow(content, true, focusedWindow);
|
||||
return [elt, focusedWindow.value];
|
||||
},
|
||||
};
|
||||
|
||||
FocusSyncHandler.init();
|
||||
|
||||
let WebProgressListener = {
|
||||
init: function() {
|
||||
this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
|
||||
|
@ -1112,7 +1112,6 @@
|
||||
"_contentTitle",
|
||||
"_characterSet",
|
||||
"_contentPrincipal",
|
||||
"_syncHandler",
|
||||
"_imageDocument",
|
||||
"_fullZoom",
|
||||
"_textZoom",
|
||||
|
@ -163,12 +163,6 @@
|
||||
onget="return this.contentWindowAsCPOW ? this.contentWindowAsCPOW.document : null"
|
||||
readonly="true"/>
|
||||
|
||||
<field name="_syncHandler">null</field>
|
||||
|
||||
<property name="syncHandler"
|
||||
onget="return this._syncHandler"
|
||||
readonly="true"/>
|
||||
|
||||
<field name="_imageDocument">null</field>
|
||||
|
||||
<property name="imageDocument"
|
||||
@ -244,7 +238,6 @@
|
||||
this.messageManager.addMessageListener("Browser:Init", this);
|
||||
this.messageManager.addMessageListener("DOMTitleChanged", this);
|
||||
this.messageManager.addMessageListener("ImageDocumentLoaded", this);
|
||||
this.messageManager.addMessageListener("SetSyncHandler", this);
|
||||
this.messageManager.addMessageListener("DocumentInserted", this);
|
||||
this.messageManager.addMessageListener("FullZoomChange", this);
|
||||
this.messageManager.addMessageListener("TextZoomChange", this);
|
||||
@ -310,10 +303,6 @@
|
||||
};
|
||||
break;
|
||||
|
||||
case "SetSyncHandler":
|
||||
this._syncHandler = aMessage.objects.handler;
|
||||
break;
|
||||
|
||||
case "Forms:ShowDropDown": {
|
||||
Cu.import("resource://gre/modules/SelectParentHelper.jsm");
|
||||
let menulist = document.getElementById(this.getAttribute("selectmenulist"));
|
||||
|
@ -93,31 +93,6 @@ this.BrowserUtils = {
|
||||
return Services.io.newURI(aCPOWURI.spec, aCPOWURI.originCharset, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the current focus element and window. If the current focus
|
||||
* is in a content process, then this function returns CPOWs
|
||||
* (cross-process object wrappers) that refer to the focused
|
||||
* items. Note that calling this function synchronously contacts the
|
||||
* content process, which may block for a long time.
|
||||
*
|
||||
* @param document The document in question.
|
||||
* @return [focusedElement, focusedWindow]
|
||||
*/
|
||||
getFocusSync: function(document) {
|
||||
let elt = document.commandDispatcher.focusedElement;
|
||||
var window = document.commandDispatcher.focusedWindow;
|
||||
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
if (elt instanceof window.XULElement &&
|
||||
elt.localName == "browser" &&
|
||||
elt.namespaceURI == XUL_NS &&
|
||||
elt.getAttribute("remote")) {
|
||||
[elt, window] = elt.syncHandler.getFocusedElementAndWindow();
|
||||
}
|
||||
|
||||
return [elt, window];
|
||||
},
|
||||
|
||||
/**
|
||||
* For a given DOM element, returns its position in "screen"
|
||||
* coordinates. In a content process, the coordinates returned will
|
||||
@ -318,4 +293,106 @@ this.BrowserUtils = {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
getSelectionDetails: function(topWindow, aCharLen) {
|
||||
// selections of more than 150 characters aren't useful
|
||||
const kMaxSelectionLen = 150;
|
||||
const charLen = Math.min(aCharLen || kMaxSelectionLen, kMaxSelectionLen);
|
||||
|
||||
let focusedWindow = {};
|
||||
let focusedElement = Services.focus.getFocusedElementForWindow(topWindow, true, focusedWindow);
|
||||
focusedWindow = focusedWindow.value;
|
||||
|
||||
let selection = focusedWindow.getSelection();
|
||||
let selectionStr = selection.toString();
|
||||
|
||||
let collapsed = selection.isCollapsed;
|
||||
|
||||
let url;
|
||||
let linkText;
|
||||
if (selectionStr) {
|
||||
// Have some text, let's figure out if it looks like a URL that isn't
|
||||
// actually a link.
|
||||
linkText = selectionStr.trim();
|
||||
if (/^(?:https?|ftp):/i.test(linkText)) {
|
||||
try {
|
||||
url = this.makeURI(linkText);
|
||||
} catch (ex) {}
|
||||
}
|
||||
// Check if this could be a valid url, just missing the protocol.
|
||||
else if (/^(?:[a-z\d-]+\.)+[a-z]+$/i.test(linkText)) {
|
||||
// Now let's see if this is an intentional link selection. Our guess is
|
||||
// based on whether the selection begins/ends with whitespace or is
|
||||
// preceded/followed by a non-word character.
|
||||
|
||||
// selection.toString() trims trailing whitespace, so we look for
|
||||
// that explicitly in the first and last ranges.
|
||||
let beginRange = selection.getRangeAt(0);
|
||||
let delimitedAtStart = /^\s/.test(beginRange);
|
||||
if (!delimitedAtStart) {
|
||||
let container = beginRange.startContainer;
|
||||
let offset = beginRange.startOffset;
|
||||
if (container.nodeType == container.TEXT_NODE && offset > 0)
|
||||
delimitedAtStart = /\W/.test(container.textContent[offset - 1]);
|
||||
else
|
||||
delimitedAtStart = true;
|
||||
}
|
||||
|
||||
let delimitedAtEnd = false;
|
||||
if (delimitedAtStart) {
|
||||
let endRange = selection.getRangeAt(selection.rangeCount - 1);
|
||||
delimitedAtEnd = /\s$/.test(endRange);
|
||||
if (!delimitedAtEnd) {
|
||||
let container = endRange.endContainer;
|
||||
let offset = endRange.endOffset;
|
||||
if (container.nodeType == container.TEXT_NODE &&
|
||||
offset < container.textContent.length)
|
||||
delimitedAtEnd = /\W/.test(container.textContent[offset]);
|
||||
else
|
||||
delimitedAtEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (delimitedAtStart && delimitedAtEnd) {
|
||||
let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"]
|
||||
.getService(Ci.nsIURIFixup);
|
||||
try {
|
||||
url = uriFixup.createFixupURI(linkText, uriFixup.FIXUP_FLAG_NONE);
|
||||
} catch (ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try getting a selected text in text input.
|
||||
if (!selectionStr && focusedElement instanceof Ci.nsIDOMNSEditableElement) {
|
||||
// Don't get the selection for password fields. See bug 565717.
|
||||
if (focusedElement instanceof Ci.nsIDOMHTMLTextAreaElement ||
|
||||
(focusedElement instanceof Ci.nsIDOMHTMLInputElement &&
|
||||
focusedElement.mozIsTextField(true))) {
|
||||
selectionStr = focusedElement.editor.selection.toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (selectionStr) {
|
||||
if (selectionStr.length > charLen) {
|
||||
// only use the first charLen important chars. see bug 221361
|
||||
var pattern = new RegExp("^(?:\\s*.){0," + charLen + "}");
|
||||
pattern.test(selectionStr);
|
||||
selectionStr = RegExp.lastMatch;
|
||||
}
|
||||
|
||||
selectionStr = selectionStr.trim().replace(/\s+/g, " ");
|
||||
|
||||
if (selectionStr.length > charLen) {
|
||||
selectionStr = selectionStr.substr(0, charLen);
|
||||
}
|
||||
}
|
||||
|
||||
if (url && !url.host) {
|
||||
url = null;
|
||||
}
|
||||
|
||||
return { text: selectionStr, docSelectionIsCollapsed: collapsed,
|
||||
linkURL: url ? url.spec : null, linkText: url ? linkText : "" };
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user