diff -r 000000000000 -r 6474c204b198 browser/devtools/inspector/breadcrumbs.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/browser/devtools/inspector/breadcrumbs.js Wed Dec 31 06:09:35 2014 +0100
@@ -0,0 +1,708 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+const {Cc, Cu, Ci} = require("chrome");
+
+const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
+const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
+const MAX_LABEL_LENGTH = 40;
+
+let promise = require("devtools/toolkit/deprecated-sync-thenables");
+
+const LOW_PRIORITY_ELEMENTS = {
+ "HEAD": true,
+ "BASE": true,
+ "BASEFONT": true,
+ "ISINDEX": true,
+ "LINK": true,
+ "META": true,
+ "SCRIPT": true,
+ "STYLE": true,
+ "TITLE": true,
+};
+
+function resolveNextTick(value) {
+ let deferred = promise.defer();
+ Services.tm.mainThread.dispatch(() => {
+ try {
+ deferred.resolve(value);
+ } catch(ex) {
+ console.error(ex);
+ }
+ }, Ci.nsIThread.DISPATCH_NORMAL);
+ return deferred.promise;
+}
+
+///////////////////////////////////////////////////////////////////////////
+//// HTML Breadcrumbs
+
+/**
+ * Display the ancestors of the current node and its children.
+ * Only one "branch" of children are displayed (only one line).
+ *
+ * FIXME: Bug 822388 - Use the BreadcrumbsWidget in the Inspector.
+ *
+ * Mechanism:
+ * . If no nodes displayed yet:
+ * then display the ancestor of the selected node and the selected node;
+ * else select the node;
+ * . If the selected node is the last node displayed, append its first (if any).
+ */
+function HTMLBreadcrumbs(aInspector)
+{
+ this.inspector = aInspector;
+ this.selection = this.inspector.selection;
+ this.chromeWin = this.inspector.panelWin;
+ this.chromeDoc = this.inspector.panelDoc;
+ this.DOMHelpers = new DOMHelpers(this.chromeWin);
+ this._init();
+}
+
+exports.HTMLBreadcrumbs = HTMLBreadcrumbs;
+
+HTMLBreadcrumbs.prototype = {
+ get walker() this.inspector.walker,
+
+ _init: function BC__init()
+ {
+ this.container = this.chromeDoc.getElementById("inspector-breadcrumbs");
+
+ // These separators are used for CSS purposes only, and are positioned
+ // off screen, but displayed with -moz-element.
+ this.separators = this.chromeDoc.createElement("box");
+ this.separators.className = "breadcrumb-separator-container";
+ this.separators.innerHTML =
+ "" +
+ "" +
+ "";
+ this.container.parentNode.appendChild(this.separators);
+
+ this.container.addEventListener("mousedown", this, true);
+ this.container.addEventListener("keypress", this, true);
+
+ // We will save a list of already displayed nodes in this array.
+ this.nodeHierarchy = [];
+
+ // Last selected node in nodeHierarchy.
+ this.currentIndex = -1;
+
+ // By default, hide the arrows. We let the show them
+ // in case of overflow.
+ this.container.removeAttribute("overflows");
+ this.container._scrollButtonUp.collapsed = true;
+ this.container._scrollButtonDown.collapsed = true;
+
+ this.onscrollboxreflow = function() {
+ if (this.container._scrollButtonDown.collapsed)
+ this.container.removeAttribute("overflows");
+ else
+ this.container.setAttribute("overflows", true);
+ }.bind(this);
+
+ this.container.addEventListener("underflow", this.onscrollboxreflow, false);
+ this.container.addEventListener("overflow", this.onscrollboxreflow, false);
+
+ this.update = this.update.bind(this);
+ this.updateSelectors = this.updateSelectors.bind(this);
+ this.selection.on("new-node-front", this.update);
+ this.selection.on("pseudoclass", this.updateSelectors);
+ this.selection.on("attribute-changed", this.updateSelectors);
+ this.inspector.on("markupmutation", this.update);
+ this.update();
+ },
+
+ /**
+ * Include in a promise's then() chain to reject the chain
+ * when the breadcrumbs' selection has changed while the promise
+ * was outstanding.
+ */
+ selectionGuard: function() {
+ let selection = this.selection.nodeFront;
+ return (result) => {
+ if (selection != this.selection.nodeFront) {
+ return promise.reject("selection-changed");
+ }
+ return result;
+ }
+ },
+
+ /**
+ * Print any errors (except selection guard errors).
+ */
+ selectionGuardEnd: function(err) {
+ if (err != "selection-changed") {
+ console.error(err);
+ }
+ promise.reject(err);
+ },
+
+ /**
+ * Build a string that represents the node: tagName#id.class1.class2.
+ *
+ * @param aNode The node to pretty-print
+ * @returns a string
+ */
+ prettyPrintNodeAsText: function BC_prettyPrintNodeText(aNode)
+ {
+ let text = aNode.tagName.toLowerCase();
+ if (aNode.id) {
+ text += "#" + aNode.id;
+ }
+
+ if (aNode.className) {
+ let classList = aNode.className.split(/\s+/);
+ for (let i = 0; i < classList.length; i++) {
+ text += "." + classList[i];
+ }
+ }
+
+ for (let pseudo of aNode.pseudoClassLocks) {
+ text += pseudo;
+ }
+
+ return text;
+ },
+
+
+ /**
+ * Build