diff --git a/browser/devtools/styleinspector/CssRuleView.jsm b/browser/devtools/styleinspector/CssRuleView.jsm index 64d92e703ddc..263309513276 100644 --- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -732,8 +732,26 @@ CssRuleView.prototype = { }.bind(this); this._createEditors(); + + // When creating a new property, we fake the normal property + // editor behavior (focusing a property's value after entering its + // name) by responding to the name's blur event, creating the + // value editor, and grabbing focus to the value editor. But if + // focus has already moved to another document, we won't be able + // to move focus to the new editor. + // Create a focusable item at the end of the editors to catch these + // cases. + this._focusBackstop = createChild(this.element, "div", { + tabindex: 0, + }); + this._backstopHandler = function() { + // If this item is actually focused long enough to get the focus + // event, allow focus to move on out of this document. + moveFocus(this.doc.defaultView, FOCUS_FORWARD); + }.bind(this); + this._focusBackstop.addEventListener("focus", this._backstopHandler, false); }, - + /** * Update the rules for the currently highlighted element. */ @@ -762,6 +780,12 @@ CssRuleView.prototype = { this._clearRules(); this._viewedElement = null; this._elementStyle = null; + + if (this._focusBackstop) { + this._focusBackstop.removeEventListener("focus", this._backstopHandler, false); + this._backstopHandler = null; + this._focusBackstop = null; + } }, /** @@ -845,7 +869,6 @@ RuleEditor.prototype = { this.openBrace = createChild(header, "span", { class: "ruleview-ruleopen", - tabindex: "0", textContent: " {" }); diff --git a/browser/devtools/styleinspector/test/Makefile.in b/browser/devtools/styleinspector/test/Makefile.in index 031a9e83eee2..104a4a4f0f2a 100644 --- a/browser/devtools/styleinspector/test/Makefile.in +++ b/browser/devtools/styleinspector/test/Makefile.in @@ -60,6 +60,7 @@ _BROWSER_TEST_FILES = \ browser_ruleview_manipulation.js \ browser_ruleview_override.js \ browser_ruleview_ui.js \ + browser_ruleview_focus.js \ browser_bug705707_is_content_stylesheet.js \ browser_bug722196_property_view_media_queries.js \ browser_bug722196_rule_view_media_queries.js \ diff --git a/browser/devtools/styleinspector/test/browser_ruleview_focus.js b/browser/devtools/styleinspector/test/browser_ruleview_focus.js new file mode 100644 index 000000000000..188255e4b73e --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js @@ -0,0 +1,106 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that focus doesn't leave the style editor when adding a property +// (bug 719916) + +let doc; +let stylePanel; + +function waitForRuleView(aCallback) +{ + if (InspectorUI.ruleView) { + aCallback(); + return; + } + + let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject); + ruleViewFrame.addEventListener("load", function(evt) { + ruleViewFrame.removeEventListener(evt.type, arguments.callee, true); + executeSoon(function() { + aCallback(); + }); + }, true); +} + +function waitForEditorFocus(aParent, aCallback) +{ + aParent.addEventListener("focus", function onFocus(evt) { + if (evt.target.inplaceEditor) { + aParent.removeEventListener("focus", onFocus, true); + let editor = evt.target.inplaceEditor; + executeSoon(function() { + aCallback(editor); + }); + } + }, true); +} + +function openRuleView() +{ + Services.obs.addObserver(function onOpened() { + Services.obs.removeObserver(onOpened, + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + + // Highlight a node. + let node = content.document.getElementsByTagName("h1")[0]; + InspectorUI.inspectNode(node); + InspectorUI.stopInspecting(); + + // Open the rule view sidebar. + waitForRuleView(testFocus); + + InspectorUI.showSidebar(); + InspectorUI.ruleButton.click(); + + testFocus(); + }, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.openInspectorUI(); +} + +function testFocus() +{ + let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject); + let brace = ruleViewFrame.contentDocument.querySelectorAll(".ruleview-ruleclose")[0]; + waitForEditorFocus(brace.parentNode, function onNewElement(aEditor) { + aEditor.input.value = "color"; + waitForEditorFocus(brace.parentNode, function onEditingValue(aEditor) { + // If we actually get this focus we're ok. + ok(true, "We got focus."); + aEditor.input.value = "green"; + + // If we've retained focus, pressing return will start a new editor. + // If not, we'll wait here until we time out. + waitForEditorFocus(brace.parentNode, function onNewEditor(aEditor) { + aEditor.input.blur(); + finishTest(); + }); + EventUtils.sendKey("return"); + }); + EventUtils.sendKey("return"); + }); + + brace.focus(); +} + +function finishUp() +{ + doc = stylePanel = null; + gBrowser.removeCurrentTab(); + finish(); +} + +function test() +{ + waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function(evt) { + gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true); + doc = content.document; + doc.title = "Rule View Test"; + waitForFocus(openRuleView, content); + }, true); + + content.location = "data:text/html,

Some header text

"; +}