Bug 912915 - Implement a simple generic highlighter. r=jwalker

This commit is contained in:
Paul Rouget 2013-09-07 11:39:50 +02:00
Родитель 0a48e68b74
Коммит a280f4502a
19 изменённых файлов: 277 добавлений и 60 удалений

Просмотреть файл

@ -1648,7 +1648,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
(function(module) {
XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers",
"resource:///modules/devtools/LayoutHelpers.jsm");
"resource://gre/modules/devtools/LayoutHelpers.jsm");
// String used as an indication to generate default file name in the following
// format: "Screen Shot yyyy-mm-dd at HH.MM.SS.png"

Просмотреть файл

@ -19,7 +19,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
Cu.import("resource:///modules/devtools/VariablesView.jsm");

Просмотреть файл

@ -11,7 +11,7 @@ const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let promise = require("sdk/core/promise");

Просмотреть файл

@ -7,7 +7,7 @@
const {Cu, Cc, Ci} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let EventEmitter = require("devtools/shared/event-emitter");
@ -15,6 +15,19 @@ let EventEmitter = require("devtools/shared/event-emitter");
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
// add ":visited" and ":link" after bug 713106 is fixed
exports._forceBasic = {value: false};
exports.Highlighter = function Highlighter(aTarget, aInspector, aToolbox) {
if (aTarget.isLocalTab && !exports._forceBasic.value) {
return new LocalHighlighter(aTarget, aInspector, aToolbox);
} else {
return new BasicHighlighter(aTarget, aInspector, aToolbox);
}
}
exports.LocalHighlighter = LocalHighlighter;
exports.BasicHighlighter = BasicHighlighter;
/**
* A highlighter mechanism.
*
@ -71,7 +84,7 @@ const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
* @param aInspector Inspector panel.
* @param aToolbox The toolbox holding the inspector.
*/
function Highlighter(aTarget, aInspector, aToolbox)
function LocalHighlighter(aTarget, aInspector, aToolbox)
{
this.target = aTarget;
this.tab = aTarget.tab;
@ -86,14 +99,12 @@ function Highlighter(aTarget, aInspector, aToolbox)
this._init();
}
exports.Highlighter = Highlighter;
Highlighter.prototype = {
LocalHighlighter.prototype = {
get selection() {
return this.inspector.selection;
},
_init: function Highlighter__init()
_init: function LocalHighlighter__init()
{
this.toggleLockState = this.toggleLockState.bind(this);
this.unlockAndFocus = this.unlockAndFocus.bind(this);
@ -157,7 +168,7 @@ Highlighter.prototype = {
/**
* Destroy the nodes. Remove listeners.
*/
destroy: function Highlighter_destroy()
destroy: function LocalHighlighter_destroy()
{
this.inspectButton.removeEventListener("command", this.unlockAndFocus);
this.inspectButton = null;
@ -195,7 +206,7 @@ Highlighter.prototype = {
/**
* Show the outline, and select a node.
*/
highlight: function Highlighter_highlight()
highlight: function LocalHighlighter_highlight()
{
if (this.selection.reason != "highlighter") {
this.lock();
@ -225,7 +236,7 @@ Highlighter.prototype = {
/**
* Update the highlighter size and position.
*/
invalidateSize: function Highlighter_invalidateSize()
invalidateSize: function LocalHighlighter_invalidateSize()
{
let canHiglightNode = this.selection.isNode() &&
this.selection.isConnected() &&
@ -333,7 +344,7 @@ Highlighter.prototype = {
/**
* Focus the browser before unlocking.
*/
unlockAndFocus: function Highlighter_unlockAndFocus() {
unlockAndFocus: function LocalHighlighter_unlockAndFocus() {
if (this.locked === false) return;
this.chromeWin.focus();
this.unlock();
@ -342,7 +353,7 @@ Highlighter.prototype = {
/**
* Hide the infobar
*/
hideInfobar: function Highlighter_hideInfobar() {
hideInfobar: function LocalHighlighter_hideInfobar() {
this.nodeInfo.container.setAttribute("force-transitions", "true");
this.nodeInfo.container.setAttribute("hidden", "true");
},
@ -350,7 +361,7 @@ Highlighter.prototype = {
/**
* Show the infobar
*/
showInfobar: function Highlighter_showInfobar() {
showInfobar: function LocalHighlighter_showInfobar() {
this.nodeInfo.container.removeAttribute("hidden");
this.moveInfobar();
this.nodeInfo.container.removeAttribute("force-transitions");
@ -359,14 +370,14 @@ Highlighter.prototype = {
/**
* Hide the outline
*/
hideOutline: function Highlighter_hideOutline() {
hideOutline: function LocalHighlighter_hideOutline() {
this.outline.setAttribute("hidden", "true");
},
/**
* Show the outline
*/
showOutline: function Highlighter_showOutline() {
showOutline: function LocalHighlighter_showOutline() {
if (this._highlighting)
this.outline.removeAttribute("hidden");
},
@ -375,7 +386,7 @@ Highlighter.prototype = {
* Build the node Infobar.
*
* <box class="highlighter-nodeinfobar-container">
* <box class="Highlighter-nodeinfobar-arrow-top"/>
* <box class="highlighter-nodeinfobar-arrow-top"/>
* <hbox class="highlighter-nodeinfobar">
* <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"/>
* <hbox class="highlighter-nodeinfobar-text">
@ -386,13 +397,13 @@ Highlighter.prototype = {
* </hbox>
* <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"/>
* </hbox>
* <box class="Highlighter-nodeinfobar-arrow-bottom"/>
* <box class="highlighter-nodeinfobar-arrow-bottom"/>
* </box>
*
* @param nsIDOMElement aParent
* The container of the infobar.
*/
buildInfobar: function Highlighter_buildInfobar(aParent)
buildInfobar: function LocalHighlighter_buildInfobar(aParent)
{
let container = this.chromeDoc.createElement("box");
container.className = "highlighter-nodeinfobar-container";
@ -489,7 +500,7 @@ Highlighter.prototype = {
* @returns boolean
* True if the rectangle was highlighted, false otherwise.
*/
highlightRectangle: function Highlighter_highlightRectangle(aRect)
highlightRectangle: function LocalHighlighter_highlightRectangle(aRect)
{
if (!aRect) {
this.unhighlight();
@ -532,7 +543,7 @@ Highlighter.prototype = {
/**
* Clear the highlighter surface.
*/
unhighlight: function Highlighter_unhighlight()
unhighlight: function LocalHighlighter_unhighlight()
{
this._highlighting = false;
this.hideOutline();
@ -541,7 +552,7 @@ Highlighter.prototype = {
/**
* Update node information (tagName#id.class)
*/
updateInfobar: function Highlighter_updateInfobar()
updateInfobar: function LocalHighlighter_updateInfobar()
{
if (!this.selection.isElementNode()) {
this.nodeInfo.tagNameLabel.textContent = "";
@ -577,7 +588,7 @@ Highlighter.prototype = {
/**
* Move the Infobar to the right place in the highlighter.
*/
moveInfobar: function Highlighter_moveInfobar()
moveInfobar: function LocalHighlighter_moveInfobar()
{
if (this._highlightRect) {
let winHeight = this.win.innerHeight * this.zoom;
@ -644,7 +655,7 @@ Highlighter.prototype = {
/**
* Store page zoom factor.
*/
computeZoomFactor: function Highlighter_computeZoomFactor() {
computeZoomFactor: function LocalHighlighter_computeZoomFactor() {
this.zoom =
this.win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
@ -654,7 +665,7 @@ Highlighter.prototype = {
/////////////////////////////////////////////////////////////////////////
//// Event Handling
attachMouseListeners: function Highlighter_attachMouseListeners()
attachMouseListeners: function LocalHighlighter_attachMouseListeners()
{
this.browser.addEventListener("mousemove", this, true);
this.browser.addEventListener("click", this, true);
@ -663,7 +674,7 @@ Highlighter.prototype = {
this.browser.addEventListener("mouseup", this, true);
},
detachMouseListeners: function Highlighter_detachMouseListeners()
detachMouseListeners: function LocalHighlighter_detachMouseListeners()
{
this.browser.removeEventListener("mousemove", this, true);
this.browser.removeEventListener("click", this, true);
@ -672,14 +683,14 @@ Highlighter.prototype = {
this.browser.removeEventListener("mouseup", this, true);
},
attachPageListeners: function Highlighter_attachPageListeners()
attachPageListeners: function LocalHighlighter_attachPageListeners()
{
this.browser.addEventListener("resize", this, true);
this.browser.addEventListener("scroll", this, true);
this.browser.addEventListener("MozAfterPaint", this, true);
},
detachPageListeners: function Highlighter_detachPageListeners()
detachPageListeners: function LocalHighlighter_detachPageListeners()
{
this.browser.removeEventListener("resize", this, true);
this.browser.removeEventListener("scroll", this, true);
@ -692,7 +703,7 @@ Highlighter.prototype = {
* @param nsIDOMEvent aEvent
* The DOM event object.
*/
handleEvent: function Highlighter_handleEvent(aEvent)
handleEvent: function LocalHighlighter_handleEvent(aEvent)
{
switch (aEvent.type) {
case "click":
@ -723,7 +734,7 @@ Highlighter.prototype = {
* Disable the CSS transitions for a short time to avoid laggy animations
* during scrolling or resizing.
*/
brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
brieflyDisableTransitions: function LocalHighlighter_brieflyDisableTransitions()
{
if (this.transitionDisabler) {
this.chromeWin.clearTimeout(this.transitionDisabler);
@ -742,7 +753,7 @@ Highlighter.prototype = {
/**
* Don't listen to page events while inspecting with the mouse.
*/
brieflyIgnorePageEvents: function Highlighter_brieflyIgnorePageEvents()
brieflyIgnorePageEvents: function LocalHighlighter_brieflyIgnorePageEvents()
{
// The goal is to keep smooth animations while inspecting.
// CSS Transitions might be interrupted because of a MozAfterPaint
@ -773,7 +784,7 @@ Highlighter.prototype = {
* @param nsIDOMEvent aEvent
* The DOM event.
*/
handleClick: function Highlighter_handleClick(aEvent)
handleClick: function LocalHighlighter_handleClick(aEvent)
{
// Stop inspection when the user clicks on a node.
if (aEvent.button == 0) {
@ -791,7 +802,7 @@ Highlighter.prototype = {
* @param nsiDOMEvent aEvent
* The MouseEvent triggering the method.
*/
handleMouseMove: function Highlighter_handleMouseMove(aEvent)
handleMouseMove: function LocalHighlighter_handleMouseMove(aEvent)
{
let doc = aEvent.target.ownerDocument;
@ -807,13 +818,58 @@ Highlighter.prototype = {
},
};
// BasicHighlighter. Doesn't implement any fancy features. Just change
// the outline of the selected node. Works with remote target.
function BasicHighlighter(aTarget, aInspector)
{
this.walker = aInspector.walker;
this.selection = aInspector.selection;
this.highlight = this.highlight.bind(this);
this.selection.on("new-node-front", this.highlight);
EventEmitter.decorate(this);
this.locked = true;
}
BasicHighlighter.prototype = {
destroy: function() {
this.selection.off("new-node-front", this.highlight);
this.walker = null;
this.selection = null;
},
toggleLockState: function() {
this.locked = !this.locked;
if (this.locked) {
this.walker.cancelPick();
} else {
this.emit("unlocked");
this.walker.pick().then(
(node) => this._onPick(node),
() => this._onPick(null)
);
}
},
highlight: function() {
this.walker.highlight(this.selection.nodeFront);
},
_onPick: function(node) {
if (node) {
this.selection.setNodeFront(node);
}
this.locked = true;
this.emit("locked");
},
hide: function() {},
show: function() {},
}
///////////////////////////////////////////////////////////////////////////
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
});
XPCOMUtils.defineLazyGetter(Highlighter.prototype, "strings", function () {
XPCOMUtils.defineLazyGetter(LocalHighlighter.prototype, "strings", function () {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/inspector.properties");
});

Просмотреть файл

@ -82,20 +82,6 @@ InspectorPanel.prototype = {
this.scheduleLayoutChange = this.scheduleLayoutChange.bind(this);
this.browser.addEventListener("resize", this.scheduleLayoutChange, true);
this.highlighter = new Highlighter(this.target, this, this._toolbox);
let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
button.hidden = false;
this.onLockStateChanged = function() {
if (this.highlighter.locked) {
button.removeAttribute("checked");
this._toolbox.raise();
} else {
button.setAttribute("checked", "true");
}
}.bind(this);
this.highlighter.on("locked", this.onLockStateChanged);
this.highlighter.on("unlocked", this.onLockStateChanged);
// Show a warning when the debugger is paused.
// We show the warning only when the inspector
// is selected.
@ -124,6 +110,19 @@ InspectorPanel.prototype = {
this.updateDebuggerPausedWarning();
}
this.highlighter = new Highlighter(this.target, this, this._toolbox);
let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
this.onLockStateChanged = function() {
if (this.highlighter.locked) {
button.removeAttribute("checked");
this._toolbox.raise();
} else {
button.setAttribute("checked", "true");
}
}.bind(this);
this.highlighter.on("locked", this.onLockStateChanged);
this.highlighter.on("unlocked", this.onLockStateChanged);
this._initMarkup();
this.isReady = false;

Просмотреть файл

@ -71,7 +71,6 @@
<toolbarbutton id="inspector-inspect-toolbutton"
tooltiptext="&inspector.selectButton.tooltip;"
class="devtools-toolbarbutton"
hidden="true"
oncommand="inspector.highlighter.toggleLockState()"/>
<arrowscrollbox id="inspector-breadcrumbs"
class="breadcrumbs-widget-container"

Просмотреть файл

@ -41,5 +41,6 @@ MOCHITEST_BROWSER_FILES := \
browser_inspector_select_last_selected.js \
browser_inspector_select_last_selected.html \
browser_inspector_select_last_selected2.html \
browser_inspector_basic_highlighter.js \
head.js \
$(NULL)

Просмотреть файл

@ -0,0 +1,60 @@
/* 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/. */
function test()
{
let inspector, doc;
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {require} = devtools;
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
waitForFocus(setupTest, content);
}, true);
content.location = "data:text/html,<h1>foo<h1><h2>bar</h2>";
function setupTest()
{
let h = require("devtools/inspector/highlighter");
h._forceBasic.value = true;
openInspector(runTests);
}
function runTests(aInspector)
{
inspector = aInspector;
let h1 = doc.querySelector("h1");
inspector.selection.once("new-node-front", () => executeSoon(testH1Selected));
inspector.selection.setNode(h1);
}
function testH1Selected() {
let h1 = doc.querySelector("h1");
let nodes = doc.querySelectorAll(":-moz-devtools-highlighted");
is(nodes.length, 1, "only one node selected");
is(nodes[0], h1, "h1 selected");
inspector.selection.once("new-node-front", () => executeSoon(testNoNodeSelected));
inspector.selection.setNode(null);
}
function testNoNodeSelected() {
ok(doc.querySelectorAll(":-moz-devtools-highlighted").length, 0, "no node selected");
finishUp();
}
function finishUp() {
let h = require("devtools/inspector/highlighter");
h._forceBasic.value = false;
gBrowser.removeCurrentTab();
finish();
}
}

Просмотреть файл

@ -32,7 +32,7 @@ function test()
parentNode.removeChild(node);
let tmp = {};
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
// Wait for the inspector to process the mutation

Просмотреть файл

@ -32,7 +32,7 @@ function test()
iframe = null;
let tmp = {};
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
ok(!inspector.selection.isConnected(), "Selection considered as disconnected");

Просмотреть файл

@ -13,7 +13,7 @@ SimpleTest.registerCleanupFunction(() => {
let tempScope = {};
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tempScope);
let LayoutHelpers = tempScope.LayoutHelpers;
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", tempScope);

Просмотреть файл

@ -8,7 +8,7 @@
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://gre/modules/devtools/Console.jsm");

Просмотреть файл

@ -17,7 +17,7 @@ let EventEmitter = require("devtools/shared/event-emitter");
let {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
let promise = require("sdk/core/promise");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/Templater.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

Просмотреть файл

@ -26,7 +26,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
Cu.import("resource://gre/modules/jsdebugger.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");

Просмотреть файл

@ -4,7 +4,7 @@
// Tests that scrollIntoViewIfNeeded works properly.
let imported = {};
Components.utils.import("resource:///modules/devtools/LayoutHelpers.jsm",
Components.utils.import("resource://gre/modules/devtools/LayoutHelpers.jsm",
imported);
registerCleanupFunction(function () {
imported = {};

Просмотреть файл

@ -10,7 +10,7 @@ let TiltUtils = devtools.require("devtools/tilt/tilt-utils");
let {TiltVisualizer} = devtools.require("devtools/tilt/tilt-visualizer");
let tempScope = {};
Components.utils.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
Components.utils.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tempScope);
let LayoutHelpers = tempScope.LayoutHelpers;

Просмотреть файл

@ -9,7 +9,7 @@ const {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
const STACK_THICKNESS = 15;

Просмотреть файл

@ -58,6 +58,7 @@ const {LongStringActor, ShortLongString} = require("devtools/server/actors/strin
const promise = require("sdk/core/promise");
const object = require("sdk/util/object");
const events = require("sdk/event/core");
const {setTimeout, clearTimeout} = require('sdk/timers');
const { Unknown } = require("sdk/platform/xpcom");
const { Class } = require("sdk/core/heritage");
const {PageStyleActor} = require("devtools/server/actors/styles");
@ -66,9 +67,14 @@ const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
const HELPER_SHEET = "." + HIDDEN_CLASS + " { visibility: hidden !important }";
const HIGHLIGHTED_PSEUDO_CLASS = ":-moz-devtools-highlighted";
const HIGHLIGHTED_TIMEOUT = 2000;
let HELPER_SHEET = ".__fx-devtools-hide-shortcut__ { visibility: hidden !important } ";
HELPER_SHEET += ":-moz-devtools-highlighted { outline: 2px dashed #F06!important; outline-offset: -2px!important } ";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
exports.register = function(handle) {
handle.addTabActor(InspectorActor, "inspectorActor");
@ -841,6 +847,94 @@ var WalkerActor = protocol.ActorClass({
return actor;
},
/**
* Pick a node on click.
*/
_pickDeferred: null,
pick: method(function() {
if (this._pickDeferred) {
return this._pickDeferred.promise;
}
this._pickDeferred = promise.defer();
let window = this.rootDoc.defaultView;
let isTouch = 'ontouchstart' in window;
let event = isTouch ? 'touchstart' : 'click';
this._onPick = function(e) {
e.stopImmediatePropagation();
e.preventDefault();
window.removeEventListener(event, this._onPick, true);
let u = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let x, y;
if (isTouch) {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
} else {
x = e.clientX;
y = e.clientY;
}
let node = u.elementFromPoint(x, y, false, false);
node = this._ref(node);
let newParents = this.ensurePathToRoot(node);
this._pickDeferred.resolve({
node: node,
newParents: [parent for (parent of newParents)]
});
this._pickDeferred = null;
}.bind(this);
window.addEventListener(event, this._onPick, true);
return this._pickDeferred.promise;
}, { request: { }, response: RetVal("disconnectedNode") }),
cancelPick: method(function() {
if (this._pickDeferred) {
let window = this.rootDoc.defaultView;
let isTouch = 'ontouchstart' in window;
let event = isTouch ? 'touchstart' : 'click';
window.removeEventListener(event, this._onPick, true);
this._pickDeferred.resolve(null);
this._pickDeferred = null;
}
}),
/**
* Simple highlight mechanism.
*/
_unhighlight: function() {
clearTimeout(this._highlightTimeout);
if (!this.rootDoc) {
return;
}
let nodes = this.rootDoc.querySelectorAll(HIGHLIGHTED_PSEUDO_CLASS);
for (let node of nodes) {
DOMUtils.removePseudoClassLock(node, HIGHLIGHTED_PSEUDO_CLASS);
}
},
highlight: method(function(node) {
this._installHelperSheet(node);
this._unhighlight();
if (!node ||
!node.rawNode ||
node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
return;
}
LayoutHelpers.scrollIntoViewIfNeeded(node.rawNode);
DOMUtils.addPseudoClassLock(node.rawNode, HIGHLIGHTED_PSEUDO_CLASS);
this._highlightTimeout = setTimeout(this._unhighlight.bind(this), HIGHLIGHTED_TIMEOUT);
}, { request: { node: Arg(0, "nullable:domnode") }}),
/**
* Watch the given document node for mutations using the DOM observer
* API.
@ -1742,6 +1836,14 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
// Set to true if cleanup should be requested after every mutation list.
autoCleanup: true,
pick: protocol.custom(function() {
return this._pick().then(response => {
return response.node;
});
}, {
impl: "_pick"
}),
initialize: function(client, form) {
this._rootNodeDeferred = promise.defer();
protocol.Front.prototype.initialize.call(this, client, form);