Index: lib/child/selection.js |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/lib/child/selection.js |
@@ -0,0 +1,285 @@ |
+/* |
saroyanm
2016/11/23 17:44:39
I thought that this code belongs to Aardvark ?
Wha
Wladimir Palant
2016/11/24 14:02:01
Yes, E10S is the reason for this whole change. Any
|
+ * This Source Code is subject to the terms of the Mozilla Public License |
+ * version 2.0 (the "License"). You can obtain a copy of the License at |
+ * http://mozilla.org/MPL/2.0/. |
+ */ |
+ |
+"use strict"; |
+ |
+let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); |
+ |
+let messageManager = require("messageManager"); |
+let { |
+ createElement, getWindowSize, getParentElement, getElementPosition |
+} = require("./utils"); |
+ |
+let state = exports.state = {}; |
Wladimir Palant
2016/11/17 13:54:48
Original code was storing the selection state as p
saroyanm
2016/11/23 17:44:39
We are setting the properties of state in other fu
Wladimir Palant
2016/11/24 14:02:02
Setting them here doesn't make sense - the definit
|
+ |
+messageManager.addMessageListener("ElemHideHelper:StartSelection", startSelection); |
+ |
+onShutdown.add(() => |
+{ |
+ messageManager.removeMessageListener("ElemHideHelper:StartSelection", startSelection); |
+ |
+ stopSelection(); |
+}); |
+ |
+function startSelection(message) |
+{ |
+ stopSelection(); |
+ |
+ let outerWindowID = message.data; |
+ let wnd = Services.wm.getOuterWindowWithId(outerWindowID); |
+ if (!wnd || !canSelect(wnd)) |
+ return; |
+ |
+ state.window = wnd; |
+ state.id = outerWindowID; |
+ |
+ wnd.addEventListener("click", onMouseClick, true); |
+ wnd.addEventListener("wheel", onMouseScroll, true); |
saroyanm
2016/11/23 17:44:39
It's a deprecated event:
https://developer.mozilla
Wladimir Palant
2016/11/24 14:02:02
That page is about the mousewheel event, not wheel
|
+ wnd.addEventListener("mousemove", onMouseMove, true); |
+ wnd.addEventListener("pagehide", onPageHide, true); |
+ |
+ wnd.focus(); |
+ |
+ let doc = wnd.document; |
+ let {elementMarkerClass} = require("info"); |
+ state.boxElement = 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"}) |
+ ]) |
+ ]); |
+ |
+ // Make sure to select some element immeditely (whichever is in the center of the browser window) |
+ let [wndWidth, wndHeight] = getWindowSize(wnd); |
+ state.isUserSelected = false; |
+ onMouseMove({clientX: wndWidth / 2, clientY: wndHeight / 2, screenX: -1, screenY: -1, target: null}); |
+ |
+ messageManager.sendAsyncMessage("ElemHideHelper:SelectionStarted"); |
+} |
+ |
+function stopSelection() |
+{ |
+ if (!state.boxElement) |
+ return; |
+ |
+ hideSelection(); |
+ |
+ let wnd = state.window; |
+ wnd.removeEventListener("click", onMouseClick, true); |
+ wnd.removeEventListener("wheel", onMouseScroll, true); |
+ wnd.removeEventListener("mousemove", onMouseMove, true); |
+ wnd.removeEventListener("pagehide", onPageHide, true); |
+ |
+ for (let key of Object.keys(state)) |
+ delete state[key]; |
+ |
+ messageManager.sendAsyncMessage("ElemHideHelper:SelectionStopped"); |
+} |
+exports.stopSelection = stopSelection; |
+ |
+function canSelect(wnd) |
+{ |
+ let acceptlocalfiles; |
saroyanm
2016/11/23 17:44:39
Nit: camelCase is missing
Wladimir Palant
2016/11/24 14:02:02
Done.
|
+ try |
+ { |
+ acceptlocalfiles = Services.prefs.getBoolPref("extensions.elemhidehelper.acceptlocalfiles"); |
saroyanm
2016/11/23 17:44:39
Nit: exceeding 80 chars.
Wladimir Palant
2016/11/24 14:02:01
Done.
|
+ } |
+ catch (e) |
+ { |
+ acceptlocalfiles = false; |
+ } |
+ |
+ if (!acceptlocalfiles) |
+ { |
+ let localSchemes; |
+ try |
+ { |
+ localSchemes = new Set( |
+ Services.prefs.getCharPref("extensions.adblockplus.whitelistschemes") |
+ .split(/\s+/) |
+ ); |
Wladimir Palant
2016/11/17 13:54:48
This changes the original logic which was simply h
|
+ } |
+ catch (e) |
+ { |
+ localSchemes = new Set(); |
+ } |
+ |
+ if (localSchemes.has(wnd.location.protocol.replace(/:$/, ""))) |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+function getElementLabel(elem) |
+{ |
+ let tagName = elem.localName; |
saroyanm
2016/11/23 17:44:39
localName is a deprecated property:
https://develo
Wladimir Palant
2016/11/24 14:02:02
That would have been surprising - the docs say tha
|
+ 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; |
saroyanm
2016/11/23 17:44:39
Weak suggestion: What about mentioning that it's n
Wladimir Palant
2016/11/24 14:02:02
Hasn't lead to any confusion so far, we can just a
|
+ |
+ return [tagName, addition]; |
+} |
+ |
+function setAnchorElement(anchor) |
saroyanm
2016/11/23 17:44:39
What is the anchor element ? I assume it's not rel
Wladimir Palant
2016/11/24 14:02:01
Nope. I've documented the state properties now, sh
|
+{ |
+ state.anchorElement = anchor; |
+ |
+ let newSelection = anchor; |
+ if (state.isUserSelected) |
+ { |
+ // User chose an element via wider/narrower commands, keep the selection if |
+ // our new anchor is still a child of that element |
+ let e = newSelection; |
+ while (e && e != state.selectedElement) |
+ e = getParentElement(e); |
+ |
+ if (e) |
+ newSelection = state.selectedElement; |
+ else |
+ state.isUserSelected = false; |
+ } |
+ |
+ selectElement(newSelection); |
+} |
+exports.setAnchorElement = setAnchorElement; |
+ |
+function selectElement(elem) |
+{ |
+ state.selectedElement = elem; |
+ state.prevSelectionUpdate = Date.now(); |
+ |
+ let border = state.boxElement.querySelector(".ehh-border"); |
+ let label = state.boxElement.querySelector(".ehh-label"); |
+ let labelTag = state.boxElement.querySelector(".ehh-labelTag"); |
+ let labelAddition = state.boxElement.querySelector(".ehh-labelAddition"); |
+ |
+ let doc = state.window.document; |
+ let [wndWidth, wndHeight] = getWindowSize(state.window); |
+ |
+ let pos = getElementPosition(elem); |
+ state.boxElement.style.left = Math.min(pos.left - 1, wndWidth - 2) + "px"; |
saroyanm
2016/11/23 17:44:38
Is this approach even working with RTL websites ?
Wladimir Palant
2016/11/24 14:02:01
Well, it works with RTL websites. Our UI is always
saroyanm
2016/11/25 16:18:13
Here you go: #4665
|
+ state.boxElement.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] = 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(state.boxElement); |
+ |
+ state.prevPos = pos; |
+ state.window.addEventListener("MozAfterPaint", onAfterPaint, false); |
+} |
+exports.selectElement = selectElement; |
+ |
+function hideSelection() |
+{ |
+ if (!Cu.isDeadWrapper(state.boxElement) && state.boxElement.parentNode) |
+ state.boxElement.parentNode.removeChild(state.boxElement); |
+ |
+ if (!Cu.isDeadWrapper(state.window)) |
+ state.window.removeEventListener("MozAfterPaint", onAfterPaint, false); |
+} |
+ |
+/****************** |
+ * Event handlers * |
+ ******************/ |
+ |
+function onMouseClick(event) |
+{ |
+ if (event.button != 0 || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) |
+ return; |
+ |
+ require("./commands").select(); |
+ event.preventDefault(); |
+} |
+ |
+function onMouseScroll(event) |
+{ |
+ if (!event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) |
+ return; |
+ |
+ let delta = event.deltaY || event.deltaX; |
Wladimir Palant
2016/11/17 13:54:48
This was dead code originally, using DOMMouseScrol
saroyanm
2016/11/23 17:44:38
Acknowledged.
|
+ if (!delta) |
+ return; |
+ |
+ let commands = require("./commands"); |
saroyanm
2016/11/23 17:44:38
Detail: we already loading the module in onMouseCl
Wladimir Palant
2016/11/24 14:02:01
That would create a circular reference.
|
+ if (delta > 0) |
+ commands.wider(); |
+ else |
+ commands.narrower(); |
+ event.preventDefault(); |
+} |
+ |
+function onMouseMove(event) |
+{ |
+ hideSelection(); |
+ |
+ 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; |
+ } |
+ |
+ // Get the element matching the coordinates, probably within a frame |
+ let elem = state.window.document.elementFromPoint(x, y); |
+ while (elem && "contentWindow" in elem && canSelect(elem.contentWindow)) |
+ { |
+ let rect = elem.getBoundingClientRect(); |
+ x -= rect.left; |
+ y -= rect.top; |
+ elem = elem.contentWindow.document.elementFromPoint(x, y); |
+ } |
+ |
+ if (elem) |
+ { |
+ if (!state.lockedAnchor) |
+ setAnchorElement(elem); |
+ else |
+ { |
+ state.lockedAnchor = elem; |
+ selectElement(state.selectedElement); |
+ } |
+ } |
+} |
+ |
+function onPageHide(event) |
+{ |
+ stopSelection(); |
+} |
+ |
+function onAfterPaint(event) |
Wladimir Palant
2016/11/17 13:54:48
This code path is untested, I'm not sure whether i
saroyanm
2016/11/23 17:44:39
We do have "onMouseScroll" function, I thought it
Wladimir Palant
2016/11/24 14:02:02
Given that the purpose of this function is selecti
|
+{ |
+ // Don't update position too often |
+ if (state.selectedElement && Date.now() - state.prevSelectionUpdate > 20) |
+ { |
+ let pos = getElementPosition(state.selectedElement); |
+ if (!state.prevPos || state.prevPos.left != pos.left || |
+ state.prevPos.right != pos.right || state.prevPos.top != pos.top || |
+ state.prevPos.bottom != pos.bottom) |
+ { |
+ selectElement(state.selectedElement); |
+ } |
+ } |
+} |