diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 704f981e6818..20ab82a900b5 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1477,7 +1477,9 @@ var gBrowserInit = { #ifdef MENUBAR_CAN_AUTOHIDE document.getElementById("appmenu_styleeditor").hidden = false; #endif - document.getElementById("developer-toolbar-styleeditor").hidden = false; + // We don't show the Style Editor button in the developer toolbar for now. + // See bug 771203 + // document.getElementById("developer-toolbar-styleeditor").hidden = false; } #ifdef MENUBAR_CAN_AUTOHIDE diff --git a/browser/base/content/newtab/cells.js b/browser/base/content/newtab/cells.js index 3c59e3e7ac29..23cbd23bfdbc 100644 --- a/browser/base/content/newtab/cells.js +++ b/browser/base/content/newtab/cells.js @@ -98,11 +98,11 @@ Cell.prototype = { * Handles all cell events. */ handleEvent: function Cell_handleEvent(aEvent) { - // We're not responding to external drag/drop events + // We're not responding to external drag/drop events // when our parent window is in private browsing mode. - if (inPrivateBrowsingMode && !gDrag.draggedSite) + if (inPrivateBrowsingMode() && !gDrag.draggedSite) return; - + if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent)) return; diff --git a/browser/base/content/newtab/newTab.js b/browser/base/content/newtab/newTab.js index f0c5f1953ed8..6b42d06fde38 100644 --- a/browser/base/content/newtab/newTab.js +++ b/browser/base/content/newtab/newTab.js @@ -22,19 +22,6 @@ let { blockedLinks: gBlockedLinks } = NewTabUtils; -let chromeWin = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - .wrappedJSObject; - -let inPrivateBrowsingMode = false; - -if ("gPrivateBrowsingUI" in chromeWin) - inPrivateBrowsingMode = chromeWin.gPrivateBrowsingUI.privateWindow; - XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() { return Services.strings. createBundle("chrome://browser/locale/newTab.properties"); @@ -42,6 +29,21 @@ XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() { function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name); +function inPrivateBrowsingMode() { + let chromeWin = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow) + .wrappedJSObject; + + if ("gPrivateBrowsingUI" in chromeWin) + return chromeWin.gPrivateBrowsingUI.privateWindow; + + return false; +} + const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml"; #include batch.js diff --git a/browser/devtools/highlighter/InsideOutBox.jsm b/browser/devtools/highlighter/InsideOutBox.jsm index 5b07e4b10532..b15dd5ebf873 100644 --- a/browser/devtools/highlighter/InsideOutBox.jsm +++ b/browser/devtools/highlighter/InsideOutBox.jsm @@ -214,12 +214,13 @@ InsideOutBox.prototype = this.selectObjectBox(objectBox, forceOpen); if (makeBoxVisible) { this.openObjectBox(objectBox); - if (scrollIntoView) { - // We want to center the label of the element, not the whole tag - // (which includes all of its children, and is vertically huge). - LayoutHelpers.scrollIntoViewIfNeeded(objectBox.firstElementChild); - } } + if (scrollIntoView) { + // We want to center the label of the element, not the whole tag + // (which includes all of its children, and is vertically huge). + LayoutHelpers.scrollIntoViewIfNeeded(objectBox.firstElementChild); + } + return objectBox; }, @@ -340,6 +341,141 @@ InsideOutBox.prototype = } }, + /** + * Returns the next object box in the tree for navigation purposes. + */ + nextObjectBox: function IOBox_nextObjectBox(aBoxObject) + { + let candidate; + let boxObject = aBoxObject || this.selectedObjectBox; + if (!boxObject) + return this.rootObjectBox; + + // If expanded, return the first child. + let isOpen = this.view.hasClass(boxObject, "open"); + let childObjectBox = this.getChildObjectBox(boxObject); + if (isOpen && childObjectBox && childObjectBox.firstChild) { + candidate = childObjectBox.firstChild; + } else { + // Otherwise we get the next available sibling. + while (boxObject) { + if (boxObject.nextSibling) { + boxObject = boxObject.nextSibling; + break; + } + boxObject = this.getParentObjectBox(boxObject); + } + candidate = boxObject; + } + + // If the node is not an element (comments or text nodes), we + // jump to the next line. + if (candidate && + candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) { + return this.nextObjectBox(candidate); + } + + return candidate; + }, + + /** + * Returns the next object in the tree for navigation purposes. + */ + nextObject: function IOBox_nextObject() + { + let next = this.nextObjectBox(); + return next ? next.repObject : null; + }, + + /** + * Returns the object that is below the selection. + * + * @param aDistance Number of lines to jump. + */ + farNextObject: function IOBox_farPreviousProject(aDistance) + { + let boxObject = this.selectedObjectBox; + while (aDistance-- > 0) { + let newBoxObject = this.nextObjectBox(boxObject); + if (!newBoxObject) { + break; + } + boxObject = newBoxObject; + } + return boxObject ? boxObject.repObject : null; + }, + + /** + * Returns the last visible child box of an object box. + */ + lastVisible: function IOBox_lastVisibleChild(aNode) + { + if (!this.view.hasClass(aNode, "open")) + return aNode; + + let childBox = this.getChildObjectBox(aNode); + if (!childBox || !childBox.lastChild) + return aNode; + + return this.lastVisible(childBox.lastChild); + }, + + /** + * Returns the previous object box in the tree for navigation purposes. + */ + previousObjectBox: function IOBox_previousObjectBox(aBoxObject) + { + let boxObject = aBoxObject || this.selectedObjectBox; + if (!boxObject) + return this.rootObjectBox; + + let candidate; + let sibling = boxObject.previousSibling; + if (sibling) { + candidate = this.lastVisible(sibling); + } else { + candidate = this.getParentObjectBox(boxObject); + } + + // If the node is not an element (comments or text nodes), we + // jump to the previous line. + if (candidate && + candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) { + return this.previousObjectBox(candidate); + } + + return candidate; + }, + + /** + * Returns the previous object in the tree for navigation purposes. + */ + previousObject: function IOBox_previousObject() + { + let boxObject = this.previousObjectBox(); + return boxObject ? boxObject.repObject : null; + }, + + /** + * Returns the object that is above the selection. + * + * @param aDistance Number of lines to jump. + */ + farPreviousObject: function IOBox_farPreviousProject(aDistance) + { + let boxObject = this.selectedObjectBox; + while (aDistance-- > 0) { + let newBoxObject = this.previousObjectBox(boxObject); + if (!newBoxObject) { + break; + } + boxObject = newBoxObject; + if (boxObject === this.rootObjectBox) + break; + } + return boxObject ? boxObject.repObject : null; + }, + /** * Open the ancestors of the given object box. * @param aObjectBox diff --git a/browser/devtools/highlighter/TreePanel.jsm b/browser/devtools/highlighter/TreePanel.jsm index 991388ac7026..55de7dc6f030 100644 --- a/browser/devtools/highlighter/TreePanel.jsm +++ b/browser/devtools/highlighter/TreePanel.jsm @@ -5,11 +5,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const Cu = Components.utils; +const Ci = Components.interfaces; Cu.import("resource:///modules/domplate.jsm"); Cu.import("resource:///modules/InsideOutBox.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource:///modules/inspector.jsm"); +Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); var EXPORTED_SYMBOLS = ["TreePanel", "DOMHelpers"]; @@ -80,6 +82,8 @@ TreePanel.prototype = { this.ioBox = new InsideOutBox(this, this.treePanelDiv); this.ioBox.createObjectBox(this.IUI.win.document.documentElement); this.treeLoaded = true; + this._boundTreeKeyPress = this.onTreeKeyPress.bind(this); + this.treeIFrame.addEventListener("keypress", this._boundTreeKeyPress.bind(this), true); this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false); this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false); this.treeIFrame.focus(); @@ -182,6 +186,7 @@ TreePanel.prototype = { this.treePanelDiv.ownerPanel = null; let parent = this.treePanelDiv.parentNode; parent.removeChild(this.treePanelDiv); + this.treeIFrame.removeEventListener("keypress", this._boundTreeKeyPress, true); delete this.treePanelDiv; delete this.treeBrowserDocument; } @@ -272,8 +277,7 @@ TreePanel.prototype = { if (this.IUI.inspecting) { this.IUI.stopInspecting(true); } else { - this.IUI.select(node, true, false); - this.IUI.highlighter.highlight(node); + this.navigate(node); } } } @@ -316,6 +320,52 @@ TreePanel.prototype = { } }, + navigate: function TP_navigate(node) + { + if (!node) + return; + this.ioBox.select(node, false, false, true); + + if (this.IUI.highlighter.isNodeHighlightable(node)) { + this.IUI.select(node, true, false, "treepanel"); + this.IUI.highlighter.highlight(node); + } + }, + + onTreeKeyPress: function TP_onTreeKeyPress(aEvent) + { + let handled = true; + switch(aEvent.keyCode) { + case Ci.nsIDOMKeyEvent.DOM_VK_LEFT: + this.ioBox.contractObjectBox(this.ioBox.selectedObjectBox); + break; + case Ci.nsIDOMKeyEvent.DOM_VK_RIGHT: + this.ioBox.expandObjectBox(this.ioBox.selectedObjectBox); + break; + case Ci.nsIDOMKeyEvent.DOM_VK_UP: + this.navigate(this.ioBox.previousObject()); + break; + case Ci.nsIDOMKeyEvent.DOM_VK_DOWN: + this.navigate(this.ioBox.nextObject()); + break; + case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP: + this.navigate(this.ioBox.farPreviousObject(10)); + break; + case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN: + this.navigate(this.ioBox.farNextObject(10)); + break; + case Ci.nsIDOMKeyEvent.DOM_VK_HOME: + this.navigate(this.ioBox.rootObject); + break; + default: + handled = false; + } + if (handled) { + aEvent.stopPropagation(); + aEvent.preventDefault(); + } + }, + /** * Starts the editor for an attribute name or value. * @param aAttrObj @@ -542,10 +592,10 @@ TreePanel.prototype = { * @param aNode the DOM node in the content document to select. * @param aScroll boolean scroll to the visible node? */ - select: function TP_select(aNode, aScroll) + select: function TP_select(aNode, aScroll, aFrom) { if (this.ioBox) { - this.ioBox.select(aNode, true, true, aScroll); + this.ioBox.select(aNode, true, aFrom != "treepanel", aScroll); } else { this.pendingSelection = { node: aNode, scroll: aScroll }; } diff --git a/browser/devtools/highlighter/inspector.jsm b/browser/devtools/highlighter/inspector.jsm index 987b8a635e53..4e67acf00126 100644 --- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -440,7 +440,7 @@ InspectorUI.prototype = { /** * Toggle the TreePanel. */ - toggleHTMLPanel: function TP_toggleHTMLPanel() + toggleHTMLPanel: function IUI_toggleHTMLPanel() { if (this.treePanel.isOpen()) { this.treePanel.close(); @@ -653,15 +653,10 @@ InspectorUI.prototype = { setupNavigationKeys: function IUI_setupNavigationKeys() { // UI elements that are arrow keys sensitive: - // - highlighter veil; - // - content window (when the highlighter `veil is pointer-events:none`; // - the Inspector toolbar. this.onKeypress = this.onKeypress.bind(this); - this.highlighter.highlighterContainer.addEventListener("keypress", - this.onKeypress, true); - this.win.addEventListener("keypress", this.onKeypress, true); this.toolbar.addEventListener("keypress", this.onKeypress, true); }, @@ -670,9 +665,6 @@ InspectorUI.prototype = { */ removeNavigationKeys: function IUI_removeNavigationKeys() { - this.highlighter.highlighterContainer.removeEventListener("keypress", - this.onKeypress, true); - this.win.removeEventListener("keypress", this.onKeypress, true); this.toolbar.removeEventListener("keypress", this.onKeypress, true); }, @@ -857,7 +849,7 @@ InspectorUI.prototype = { this.breadcrumbs.update(); this.chromeWin.Tilt.update(aNode); - this.treePanel.select(aNode, aScroll); + this.treePanel.select(aNode, aScroll, aFrom); this._notifySelected(aFrom); }, @@ -1924,7 +1916,7 @@ HTMLBreadcrumbs.prototype = { let classesLabel = this.IUI.chromeDoc.createElement("label"); classesLabel.className = "inspector-breadcrumbs-classes plain"; - + let pseudosLabel = this.IUI.chromeDoc.createElement("label"); pseudosLabel.className = "inspector-breadcrumbs-pseudo-classes plain"; @@ -2153,6 +2145,7 @@ HTMLBreadcrumbs.prototype = { }; button.onclick = (function _onBreadcrumbsRightClick(aEvent) { + button.focus(); if (aEvent.button == 2) { this.openSiblingMenu(button, aNode); } diff --git a/browser/devtools/highlighter/test/Makefile.in b/browser/devtools/highlighter/test/Makefile.in index bae8d3589d37..0db71e10830f 100644 --- a/browser/devtools/highlighter/test/Makefile.in +++ b/browser/devtools/highlighter/test/Makefile.in @@ -42,6 +42,8 @@ _BROWSER_FILES = \ browser_inspector_pseudoClass_menu.js \ browser_inspector_destroyselection.html \ browser_inspector_destroyselection.js \ + browser_inspector_treePanel_navigation.html \ + browser_inspector_treePanel_navigation.js \ head.js \ $(NULL) diff --git a/browser/devtools/highlighter/test/browser_inspector_bug_672902_keyboard_shortcuts.js b/browser/devtools/highlighter/test/browser_inspector_bug_672902_keyboard_shortcuts.js index 46edb5936bd8..1e11ec60e944 100644 --- a/browser/devtools/highlighter/test/browser_inspector_bug_672902_keyboard_shortcuts.js +++ b/browser/devtools/highlighter/test/browser_inspector_bug_672902_keyboard_shortcuts.js @@ -42,6 +42,8 @@ function test() // Test that navigating around without a selected node gets us to the // body element. node = doc.querySelector("body"); + let bc = InspectorUI.breadcrumbs; + bc.nodeHierarchy[bc.currentIndex].button.focus(); EventUtils.synthesizeKey("VK_RIGHT", { }); }); } diff --git a/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.html b/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.html new file mode 100644 index 000000000000..fe5561181b14 --- /dev/null +++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.html @@ -0,0 +1,26 @@ + + + + + + +
+

line1

+

line2

+

line3

+ +

line4 + line5 + line6 + + line7line8 + line9 + line10 + line11 + line12line13 +

+

line14

+

line15

+
+ + diff --git a/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.js b/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.js new file mode 100644 index 000000000000..ec3a5aed01f9 --- /dev/null +++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.js @@ -0,0 +1,103 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + + +function test() { + + waitForExplicitFinish(); + + let doc; + + let keySequence = "right down right "; + keySequence += "down down down down right "; + keySequence += "down down down right "; + keySequence += "down down down down down right "; + keySequence += "down down down down down "; + keySequence += "up up up left down home "; + keySequence += "pagedown left down down pageup pageup left down"; + + keySequence = keySequence.split(" "); + + let keySequenceRes = "body node0 node0 "; + keySequenceRes += "node1 node2 node3 node4 node4 "; + keySequenceRes += "node5 node6 node7 node7 "; + keySequenceRes += "node8 node9 node10 node11 node12 node12 "; + keySequenceRes += "node13 node14 node15 node15 node15 "; + keySequenceRes += "node14 node13 node12 node12 node14 html "; + keySequenceRes += "node7 node7 node9 node10 body html html html"; + + keySequenceRes = keySequenceRes.split(" "); + + + 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 = "http://mochi.test:8888/browser/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.html"; + + function setupTest() { + Services.obs.addObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.toggleInspectorUI(); + } + + function runTests() { + Services.obs.removeObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + Services.obs.addObserver(startNavigation, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false); + InspectorUI.select(doc.body, true, true, true); + InspectorUI.toggleHTMLPanel(); + } + + function startNavigation() { + Services.obs.removeObserver(startNavigation, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); + nextStep(0); + } + + function nextStep(cursor) { + let key = keySequence[cursor]; + let className = keySequenceRes[cursor]; + switch(key) { + case "right": + EventUtils.synthesizeKey("VK_RIGHT", {}); + break; + case "down": + EventUtils.synthesizeKey("VK_DOWN", {}); + break; + case "left": + EventUtils.synthesizeKey("VK_LEFT", {}); + break; + case "up": + EventUtils.synthesizeKey("VK_UP", {}); + break; + case "pageup": + EventUtils.synthesizeKey("VK_PAGE_UP", {}); + break; + case "pagedown": + EventUtils.synthesizeKey("VK_PAGE_DOWN", {}); + break; + case "home": + EventUtils.synthesizeKey("VK_HOME", {}); + break; + } + + executeSoon(function() { + if (cursor >= keySequence.length) { + Services.obs.addObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.closeInspectorUI(); + } else { + let node = InspectorUI.treePanel.ioBox.selectedObjectBox.repObject; + is(node.className, className, "[" + cursor + "] right node selected: " + className); + nextStep(cursor + 1); + } + }); + } + + function finishUp() { + Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED); + doc = null; + gBrowser.removeCurrentTab(); + finish(); + } +} diff --git a/browser/devtools/responsivedesign/responsivedesign.jsm b/browser/devtools/responsivedesign/responsivedesign.jsm index 7ebe7276c58b..70051907b9b9 100644 --- a/browser/devtools/responsivedesign/responsivedesign.jsm +++ b/browser/devtools/responsivedesign/responsivedesign.jsm @@ -112,11 +112,13 @@ function ResponsiveUI(aWindow, aTab) // Events this.tab.addEventListener("TabClose", this); this.tabContainer.addEventListener("TabSelect", this); - this.mainWindow.addEventListener("keypress", this.bound_onKeypress, true); + this.mainWindow.document.addEventListener("keypress", this.bound_onKeypress, false); this.buildUI(); this.checkMenus(); + this.inspectorWasOpen = this.mainWindow.InspectorUI.isInspectorOpen; + try { if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) { this.rotate(); @@ -140,6 +142,10 @@ ResponsiveUI.prototype = { * Destroy the nodes. Remove listeners. Reset the style. */ close: function RUI_unload() { + if (this.closing) + return; + this.closing = true; + this.unCheckMenus(); // Reset style of the stack. let style = "max-width: none;" + @@ -154,7 +160,7 @@ ResponsiveUI.prototype = { this.saveCurrentPreset(); // Remove listeners. - this.mainWindow.removeEventListener("keypress", this.bound_onKeypress, true); + this.mainWindow.document.removeEventListener("keypress", this.bound_onKeypress, false); this.menulist.removeEventListener("select", this.bound_presetSelected, true); this.tab.removeEventListener("TabClose", this); this.tabContainer.removeEventListener("TabSelect", this); @@ -195,9 +201,17 @@ ResponsiveUI.prototype = { onKeypress: function RUI_onKeypress(aEvent) { if (aEvent.keyCode == this.mainWindow.KeyEvent.DOM_VK_ESCAPE && this.mainWindow.gBrowser.selectedBrowser == this.browser) { - aEvent.preventDefault(); - aEvent.stopPropagation(); - this.close(); + + // If the inspector wasn't open at first but is open now, + // we don't want to close the Responsive Mode on Escape. + // We let the inspector close first. + + let isInspectorOpen = this.mainWindow.InspectorUI.isInspectorOpen; + if (this.inspectorWasOpen || !isInspectorOpen) { + aEvent.preventDefault(); + aEvent.stopPropagation(); + this.close(); + } } }, diff --git a/browser/themes/gnomestripe/devtools/debugger.css b/browser/themes/gnomestripe/devtools/debugger.css index 23886a523934..4ecdbc27e58d 100644 --- a/browser/themes/gnomestripe/devtools/debugger.css +++ b/browser/themes/gnomestripe/devtools/debugger.css @@ -274,10 +274,6 @@ list-style-image: url("chrome://browser/skin/devtools/debugger-step-out.png"); } -#step-over { - list-style-image: url("chrome://browser/skin/devtools/debugger-step-over.png"); -} - #debugger-controls > toolbarbutton { border-width: 0; -moz-border-end-width: 1px; diff --git a/browser/themes/pinstripe/devtools/debugger.css b/browser/themes/pinstripe/devtools/debugger.css index 65bff912ee63..3b9907b9ad42 100644 --- a/browser/themes/pinstripe/devtools/debugger.css +++ b/browser/themes/pinstripe/devtools/debugger.css @@ -274,10 +274,6 @@ list-style-image: url("chrome://browser/skin/devtools/debugger-step-out.png"); } -#step-over { - list-style-image: url("chrome://browser/skin/devtools/debugger-step-over.png"); -} - #debugger-controls > toolbarbutton { border-width: 0; -moz-border-end-width: 1px; diff --git a/browser/themes/winstripe/devtools/debugger.css b/browser/themes/winstripe/devtools/debugger.css index ba713133183b..1c943264d37b 100644 --- a/browser/themes/winstripe/devtools/debugger.css +++ b/browser/themes/winstripe/devtools/debugger.css @@ -277,10 +277,6 @@ list-style-image: url("chrome://browser/skin/devtools/debugger-step-out.png"); } -#step-over { - list-style-image: url("chrome://browser/skin/devtools/debugger-step-over.png"); -} - #debugger-controls > toolbarbutton { border-width: 0; -moz-border-end-width: 1px;