Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/aardvark.js

Issue 29363476: Issue 2879 - Move element selection into the content process (Closed) Base URL: https://hg.adblockplus.org/elemhidehelper
Patch Set: Addressed comments and marked extension as E10S-compatible Created Nov. 24, 2016, 2 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | lib/child/commands.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/aardvark.js
===================================================================
--- a/lib/aardvark.js
+++ b/lib/aardvark.js
@@ -3,132 +3,142 @@
* version 2.0 (the "License"). You can obtain a copy of the License at
* http://mozilla.org/MPL/2.0/.
*/
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let {Prefs} = require("prefs");
-// Make sure to stop selection when we are uninstalled
-onShutdown.add(() => Aardvark.quit());
+let messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+ .getService(Ci.nsIMessageListenerManager)
+ .QueryInterface(Ci.nsIMessageBroadcaster);
// To be replaced when selection starts
function E(id) {return null;}
-let messageCounter = 0;
-
-/*********************************
- * Minimal element creation code *
- *********************************/
-
-function createElement(doc, tagName, attrs, children)
+messageManager.addMessageListener("ElemHideHelper:SelectionStarted",
+ selectionStarted);
+messageManager.addMessageListener("ElemHideHelper:SelectionSucceeded",
+ selectionSucceeded);
+messageManager.addMessageListener("ElemHideHelper:SelectionStopped",
+ selectionStopped);
+onShutdown.add(() =>
{
- let el = doc.createElement(tagName);
- if (attrs)
- for (let key in attrs)
- el.setAttribute(key, attrs[key]);
- if (children)
- for (let child of children)
- el.appendChild(child)
- return el;
-};
+ messageManager.removeMessageListener("ElemHideHelper:SelectionStarted",
+ selectionStarted);
+ messageManager.removeMessageListener("ElemHideHelper:SelectionSucceeded",
+ selectionSucceeded);
+ messageManager.removeMessageListener("ElemHideHelper:SelectionStopped",
+ selectionStopped);
+
+ selectionStopped();
+});
+
+function selectionStarted(message)
+{
+ Aardvark.selectionStarted();
+}
+
+function selectionSucceeded(message)
+{
+ Aardvark.selectionSucceeded(message.data);
+}
+
+function selectionStopped(message)
+{
+ Aardvark.selectionStopped();
+}
/**********************************
* General element selection code *
**********************************/
let Aardvark = exports.Aardvark =
{
window: null,
browser: null,
- anchorElem: null,
- selectedElem: null,
- isUserSelected: false,
- lockedAnchor: null,
- commentElem: null,
+ rememberedWrapper: null,
mouseX: -1,
mouseY: -1,
- prevSelectionUpdate: -1,
commandLabelTimer: null,
viewSourceTimer: null,
- boxElem: null,
- paintNode: null,
- prevPos: null,
start: function(wrapper)
{
- if (!this.canSelect(wrapper.browser))
- return;
+ this.rememberedWrapper = wrapper;
+ let browser = wrapper.browser;
+ if ("selectedBrowser" in browser)
+ browser = browser.selectedBrowser;
+ messageManager.broadcastAsyncMessage(
+ "ElemHideHelper:StartSelection",
+ browser.outerWindowID
+ );
+ },
- if (this.browser)
- this.quit();
+ selectionStarted: function()
+ {
+ let wrapper = this.rememberedWrapper;
+ this.rememberedWrapper = null;
this.window = wrapper.window;
this.browser = wrapper.browser;
E = id => wrapper.E(id);
- this.browser.addEventListener("click", this.onMouseClick, true);
- this.browser.addEventListener("DOMMouseScroll", this.onMouseScroll, true);
this.browser.addEventListener("keypress", this.onKeyPress, true);
- this.browser.addEventListener("mousemove", this.onMouseMove, true);
- this.browser.addEventListener("select", this.quit, false);
- this.browser.contentWindow.addEventListener("pagehide", this.onPageHide, true);
-
- this.browser.contentWindow.focus();
-
- let doc = this.browser.contentDocument;
- let {elementMarkerClass} = require("main");
- this.boxElem = createElement(doc, "div", {"class": elementMarkerClass}, [
- createElement(doc, "div", {"class": "ehh-border"}),
- createElement(doc, "div", {"class": "ehh-label"}, [
- createElement(doc, "span", {"class": "ehh-labelTag"}),
- createElement(doc, "span", {"class": "ehh-labelAddition"})
- ])
- ]);
+ this.browser.addEventListener("mousemove", this.onMouseMove, false);
+ this.browser.addEventListener("select", this.onTabSelect, false);
this.initHelpBox();
if (Prefs.showhelp)
this.showMenu();
-
- // Make sure to select some element immeditely (whichever is in the center of the browser window)
- let [wndWidth, wndHeight] = this.getWindowSize(doc.defaultView);
- this.isUserSelected = false;
- this.onMouseMove({clientX: wndWidth / 2, clientY: wndHeight / 2, screenX: -1, screenY: -1, target: null});
},
- canSelect: function(browser)
+ selectionSucceeded: function(nodeInfo)
{
- if (!browser || !browser.contentWindow ||
- !(browser.contentDocument instanceof Ci.nsIDOMHTMLDocument))
- {
- return false;
- }
+ this.window.openDialog("chrome://elemhidehelper/content/composer.xul",
+ "_blank", "chrome,centerscreen,resizable,dialog=no", nodeInfo);
+ },
- let location = browser.contentWindow.location;
- if (location.href == "about:blank")
- return false;
+ selectionStopped: function()
+ {
+ if (!this.browser)
+ return;
- if (!Prefs.acceptlocalfiles &&
- location.hostname == "" &&
- location.protocol != "mailbox:" &&
- location.protocol != "imap:" &&
- location.protocol != "news:" &&
- location.protocol != "snews:")
- {
- return false;
- }
+ if (this.commandLabelTimer)
+ this.commandLabelTimer.cancel();
+ if (this.viewSourceTimer)
+ this.viewSourceTimer.cancel();
+ this.commandLabelTimer = null;
+ this.viewSourceTimer = null;
- return true;
+ this.hideTooltips();
+
+ this.browser.removeEventListener("keypress", this.onKeyPress, true);
+ this.browser.removeEventListener("mousemove", this.onMouseMove, false);
+ this.browser.removeEventListener("select", this.onTabSelect, false);
+
+ this.window = null;
+ this.browser = null;
+ E = id => null;
},
doCommand: function(command, event)
{
- if (this[command](this.selectedElem))
+ let showFeedback;
+ if (this.hasOwnProperty(command))
+ showFeedback = this[command]();
+ else
+ {
+ showFeedback = (command != "select" && command != "quit");
+ messageManager.broadcastAsyncMessage("ElemHideHelper:Command", command);
+ }
+
+ if (showFeedback)
{
this.showCommandLabel(this.commands[command + "_key"], this.commands[command + "_altkey"], this.commands[command + "_label"]);
if (event)
event.stopPropagation();
}
if (event)
event.preventDefault();
},
@@ -199,35 +209,16 @@ let Aardvark = exports.Aardvark =
for (let i = 0; i < tooltips.length; i++)
{
let tooltip = E(tooltips[i]);
if (tooltip)
tooltip.hidePopup();
}
},
- onMouseClick: function(event)
- {
- if (event.button != 0 || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)
- return;
-
- this.doCommand("select", event);
- },
-
- onMouseScroll: function(event)
- {
- if (!event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)
- return;
-
- if ("axis" in event && event.axis != event.VERTICAL_AXIS)
- return;
-
- this.doCommand(event.detail > 0 ? "wider" : "narrower", event);
- },
-
onKeyPress: function(event)
{
if (event.altKey || event.ctrlKey || event.metaKey)
return;
var command = null;
if (event.keyCode == event.DOM_VK_ESCAPE)
command = "quit";
@@ -241,450 +232,68 @@ let Aardvark = exports.Aardvark =
if (commands[commands[i] + "_key"] == key || commands[commands[i] + "_altkey"] == key)
command = commands[i];
}
if (command)
this.doCommand(command, event);
},
- onPageHide: function(event)
- {
- this.doCommand("quit", null);
- },
-
onMouseMove: function(event)
{
this.mouseX = event.screenX;
this.mouseY = event.screenY;
-
- this.hideSelection();
- if (!this.browser)
- {
- // hideSelection() called quit()
- return;
- }
-
- let x = event.clientX;
- let y = event.clientY;
-
- // We might have coordinates relative to a frame, recalculate relative to top window
- let node = event.target;
- while (node && node.ownerDocument && node.ownerDocument.defaultView && node.ownerDocument.defaultView.frameElement)
- {
- node = node.ownerDocument.defaultView.frameElement;
- let rect = node.getBoundingClientRect();
- x += rect.left;
- y += rect.top;
- }
-
- let elem = this.browser.contentDocument.elementFromPoint(x, y);
- while (elem && "contentDocument" in elem && this.canSelect(elem))
- {
- let rect = elem.getBoundingClientRect();
- x -= rect.left;
- y -= rect.top;
- elem = elem.contentDocument.elementFromPoint(x, y);
- }
-
- if (elem)
- {
- if (!this.lockedAnchor)
- this.setAnchorElement(elem);
- else
- {
- this.lockedAnchor = elem;
- this.selectElement(this.selectedElem);
- }
- }
},
- onAfterPaint: function()
- {
- // Don't update position too often
- if (this.selectedElem && Date.now() - this.prevSelectionUpdate > 20)
- {
- let pos = this.getElementPosition(this.selectedElem);
- if (!this.prevPos || this.prevPos.left != pos.left || this.prevPos.right != pos.right
- || this.prevPos.top != pos.top || this.prevPos.bottom != pos.bottom)
- {
- this.selectElement(this.selectedElem);
- }
- }
- },
-
- setAnchorElement: function(anchor)
+ onTabSelect: function(event)
{
- this.anchorElem = anchor;
-
- let newSelection = anchor;
- if (this.isUserSelected)
- {
- // User chose an element via wider/narrower commands, keep the selection if
- // out new anchor is still a child of that element
- let e = newSelection;
- while (e && e != this.selectedElem)
- e = this.getParentElement(e);
-
- if (e)
- newSelection = this.selectedElem;
- else
- this.isUserSelected = false;
- }
-
- this.selectElement(newSelection);
+ this.doCommand("quit", null);
},
appendDescription: function(node, value, className)
{
var descr = this.window.document.createElement("description");
descr.setAttribute("value", value);
if (className)
descr.setAttribute("class", className);
node.appendChild(descr);
},
- /**************************
- * Element marker display *
- **************************/
-
- getElementLabel: function(elem)
- {
- let tagName = elem.tagName.toLowerCase();
- let addition = "";
- if (elem.id != "")
- addition += ", id: " + elem.id;
- if (elem.className != "")
- addition += ", class: " + elem.className;
- if (elem.style.cssText != "")
- addition += ", style: " + elem.style.cssText;
-
- return [tagName, addition];
- },
-
- selectElement: function(elem)
- {
- this.selectedElem = elem;
- this.prevSelectionUpdate = Date.now();
-
- let border = this.boxElem.getElementsByClassName("ehh-border")[0];
- let label = this.boxElem.getElementsByClassName("ehh-label")[0];
- let labelTag = this.boxElem.getElementsByClassName("ehh-labelTag")[0];
- let labelAddition = this.boxElem.getElementsByClassName("ehh-labelAddition")[0];
-
- if (this.boxElem.parentNode)
- this.boxElem.parentNode.removeChild(this.boxElem);
-
- let doc = this.browser.contentDocument;
- let [wndWidth, wndHeight] = this.getWindowSize(doc.defaultView);
-
- let pos = this.getElementPosition(elem);
- this.boxElem.style.left = Math.min(pos.left - 1, wndWidth - 2) + "px";
- this.boxElem.style.top = Math.min(pos.top - 1, wndHeight - 2) + "px";
- border.style.width = Math.max(pos.right - pos.left - 2, 0) + "px";
- border.style.height = Math.max(pos.bottom - pos.top - 2, 0) + "px";
-
- [labelTag.textContent, labelAddition.textContent] = this.getElementLabel(elem);
-
- // If there is not enough space to show the label move it up a little
- if (pos.bottom < wndHeight - 25)
- label.className = "ehh-label";
- else
- label.className = "ehh-label onTop";
-
- doc.documentElement.appendChild(this.boxElem);
-
- this.paintNode = doc.defaultView;
- if (this.paintNode)
- {
- this.prevPos = pos;
- this.paintNode.addEventListener("MozAfterPaint", this.onAfterPaint, false);
- }
- },
-
- hideSelection: function()
- {
- try
- {
- if (this.boxElem.parentNode)
- this.boxElem.parentNode.removeChild(this.boxElem);
- }
- catch (e)
- {
- // Are we using CPOW whose process is gone? Quit!
- // Clear some variables to prevent recursion (quit will call us again).
- this.boxElem = {};
- this.paintNode = null;
- this.quit();
- return;
- }
-
- if (this.paintNode)
- this.paintNode.removeEventListener("MozAfterPaint", this.onAfterPaint, false);
-
- this.paintNode = null;
- this.prevPos = null;
- },
-
- getWindowSize: function(wnd)
- {
- return [wnd.innerWidth, wnd.document.documentElement.clientHeight];
- },
-
- getElementPosition: function(element)
- {
- // Restrict rectangle coordinates by the boundaries of a window's client area
- function intersectRect(rect, wnd)
- {
- let [wndWidth, wndHeight] = this.getWindowSize(wnd);
- rect.left = Math.max(rect.left, 0);
- rect.top = Math.max(rect.top, 0);
- rect.right = Math.min(rect.right, wndWidth);
- rect.bottom = Math.min(rect.bottom, wndHeight);
- }
-
- let rect = element.getBoundingClientRect();
- let wnd = element.ownerDocument.defaultView;
-
- rect = {left: rect.left, top: rect.top,
- right: rect.right, bottom: rect.bottom};
- while (true)
- {
- intersectRect.call(this, rect, wnd);
-
- if (!wnd.frameElement)
- break;
-
- // Recalculate coordinates to be relative to frame's parent window
- let frameElement = wnd.frameElement;
- wnd = frameElement.ownerDocument.defaultView;
-
- let frameRect = frameElement.getBoundingClientRect();
- let frameStyle = wnd.getComputedStyle(frameElement, null);
- let relLeft = frameRect.left + parseFloat(frameStyle.borderLeftWidth) + parseFloat(frameStyle.paddingLeft);
- let relTop = frameRect.top + parseFloat(frameStyle.borderTopWidth) + parseFloat(frameStyle.paddingTop);
-
- rect.left += relLeft;
- rect.right += relLeft;
- rect.top += relTop;
- rect.bottom += relTop;
- }
-
- return rect;
- },
-
- getParentElement: function(elem)
- {
- let result = elem.parentNode;
- if (result && result.nodeType == Ci.nsIDOMElement.DOCUMENT_NODE && result.defaultView && result.defaultView.frameElement)
- result = result.defaultView.frameElement;
-
- if (result && result.nodeType != Ci.nsIDOMElement.ELEMENT_NODE)
- return null;
-
- return result;
- },
-
/***************************
* Commands implementation *
***************************/
commands: [
"select",
"wider",
"narrower",
"lock",
"quit",
"blinkElement",
"viewSource",
"viewSourceWindow",
"showMenu"
],
- wider: function(elem)
- {
- if (!elem)
- return false;
-
- let newElem = this.getParentElement(elem);
- if (!newElem)
- return false;
-
- this.isUserSelected = true;
- this.selectElement(newElem);
- return true;
- },
-
- narrower: function(elem)
- {
- if (elem)
- {
- // Search selected element in the parent chain, starting with the anchor element.
- // We need to select the element just before the selected one.
- let e = this.anchorElem;
- let newElem = null;
- while (e && e != elem)
- {
- newElem = e;
- e = this.getParentElement(e);
- }
-
- if (!e || !newElem)
- return false;
-
- this.isUserSelected = true;
- this.selectElement(newElem);
- return true;
- }
- return false;
- },
-
- lock: function(elem)
- {
- if (!elem)
- return false;
-
- if (this.lockedAnchor)
- {
- this.setAnchorElement(this.lockedAnchor);
- this.lockedAnchor = null;
- }
- else
- this.lockedAnchor = this.anchorElem;
-
- return true;
- },
-
- quit: function()
- {
- if (!this.browser)
- return false;
-
- if ("blinkTimer" in this)
- this.stopBlinking();
-
- if (this.commandLabelTimer)
- this.commandLabelTimer.cancel();
- if (this.viewSourceTimer)
- this.viewSourceTimer.cancel();
- this.commandLabelTimer = null;
- this.viewSourceTimer = null;
-
- this.hideSelection();
- this.hideTooltips();
-
- this.browser.removeEventListener("click", this.onMouseClick, true);
- this.browser.removeEventListener("DOMMouseScroll", this.onMouseScroll, true);
- this.browser.removeEventListener("keypress", this.onKeyPress, true);
- this.browser.removeEventListener("mousemove", this.onMouseMove, true);
- this.browser.removeEventListener("select", this.quit, false);
- this.browser.contentWindow.removeEventListener("pagehide", this.onPageHide, true);
-
- this.anchorElem = null;
- this.selectedElem = null;
- this.window = null;
- this.browser = null;
- this.commentElem = null;
- this.lockedAnchor = null;
- this.boxElem = null;
- E = id => null;
- return false;
- },
-
- select: function(elem)
- {
- if (!elem || !this.window)
- return false;
-
- let messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"]
- .getService(Ci.nsIMessageBroadcaster);
- let messageId = ++messageCounter;
- let callback = (message) =>
- {
- let response = message.data;
- if (response.messageId != messageId)
- return;
-
- messageManager.removeMessageListener(
- "ElemHideHelper:GetNodeInfo:Response",
- callback
- );
-
- if (!response.nodeData)
- return;
-
- this.window.openDialog("chrome://elemhidehelper/content/composer.xul",
- "_blank", "chrome,centerscreen,resizable,dialog=no", response);
- this.quit();
- };
-
- messageManager.addMessageListener(
- "ElemHideHelper:GetNodeInfo:Response",
- callback
- );
- messageManager.broadcastAsyncMessage(
- "ElemHideHelper:GetNodeInfo",
- messageId,
- {
- element: elem
- }
- );
- return false;
- },
-
- blinkElement: function(elem)
- {
- if (!elem)
- return false;
-
- if ("blinkTimer" in this)
- this.stopBlinking();
-
- let counter = 0;
- this.blinkElem = elem;
- this.blinkOrigValue = elem.style.visibility;
- this.blinkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- this.blinkTimer.initWithCallback(function()
- {
- counter++;
- elem.style.visibility = (counter % 2 == 0 ? "visible" : "hidden");
- if (counter == 6)
- Aardvark.stopBlinking();
- }, 250, Ci.nsITimer.TYPE_REPEATING_SLACK);
-
- return true;
- },
-
- stopBlinking: function()
- {
- this.blinkTimer.cancel();
- this.blinkElem.style.visibility = this.blinkOrigValue;
-
- delete this.blinkElem;
- delete this.blinkOrigValue;
- delete this.blinkTimer;
- },
-
viewSource: function(elem)
{
if (!elem)
return false;
var sourceBox = E("ehh-viewsource");
- if (sourceBox.state == "open" && this.commentElem == elem)
+ if (sourceBox.state == "open")
{
sourceBox.hidePopup();
return true;
}
sourceBox.hidePopup();
while (sourceBox.firstElementChild)
sourceBox.removeChild(sourceBox.firstElementChild);
this.getOuterHtmlFormatted(elem, sourceBox);
- this.commentElem = elem;
let anchor = this.window.document.documentElement;
let x = this.mouseX;
let y = this.mouseY;
this.viewSourceTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.viewSourceTimer.initWithCallback(function()
{
sourceBox.showPopup(anchor, x, y, "tooltip", "topleft", "topleft");
@@ -819,10 +428,10 @@ let Aardvark = exports.Aardvark =
// Show help box
helpBox.showPopup(this.browser, -1, -1, "tooltip", "topleft", "topleft");
return true;
}
}
// Makes sure event handlers like Aardvark.onKeyPress always have the correct
// this pointer set.
-for (let method of ["onMouseClick", "onMouseScroll", "onKeyPress", "onPageHide", "onMouseMove", "onAfterPaint", "quit"])
+for (let method of ["onKeyPress", "onMouseMove", "onTabSelect"])
Aardvark[method] = Aardvark[method].bind(Aardvark);
« no previous file with comments | « no previous file | lib/child/commands.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld