author | Aleh Zasypkin <aleh.zasypkin@gmail.com> |
Fri, 17 Jan 2014 10:47:02 +0100 | |
changeset 164558 | d8ab8158574d50d702aa7711c00fb03f8aa5fcf3 |
parent 164557 | 31dca1ad8cfb22f3e15365c4dedb7399f459f7c4 |
child 164559 | de1374bf0c6edbc1e182078d15d2f99603f39dd6 |
push id | 38744 |
push user | kwierso@gmail.com |
push date | Wed, 22 Jan 2014 01:36:02 +0000 |
treeherder | mozilla-inbound@554475555e21 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mbrubeck, sfoster |
bugs | 947505 |
milestone | 29.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/metro/base/content/Util.js +++ b/browser/metro/base/content/Util.js @@ -78,40 +78,54 @@ let Util = { }, isTextInput: function isTextInput(aElement) { return ((aElement instanceof Ci.nsIDOMHTMLInputElement && aElement.mozIsTextField(false)) || aElement instanceof Ci.nsIDOMHTMLTextAreaElement); }, + /** + * Checks whether aElement's content can be edited either if it(or any of its + * parents) has "contenteditable" attribute set to "true" or aElement's + * ownerDocument is in design mode. + */ isEditableContent: function isEditableContent(aElement) { - if (!aElement) - return false; - if (aElement.isContentEditable || aElement.designMode == "on") - return true; - return false; + return !!aElement && (aElement.isContentEditable || + this.isOwnerDocumentInDesignMode(aElement)); + }, isEditable: function isEditable(aElement) { - if (!aElement) + if (!aElement) { return false; - if (this.isTextInput(aElement) || this.isEditableContent(aElement)) + } + + if (this.isTextInput(aElement) || this.isEditableContent(aElement)) { return true; + } // If a body element is editable and the body is the child of an // iframe or div we can assume this is an advanced HTML editor if ((aElement instanceof Ci.nsIDOMHTMLIFrameElement || aElement instanceof Ci.nsIDOMHTMLDivElement) && aElement.contentDocument && this.isEditableContent(aElement.contentDocument.body)) { return true; } - return aElement.ownerDocument && aElement.ownerDocument.designMode == "on"; + return false; + }, + + /** + * Checks whether aElement's owner document has design mode turned on. + */ + isOwnerDocumentInDesignMode: function(aElement) { + return !!aElement && !!aElement.ownerDocument && + aElement.ownerDocument.designMode == "on"; }, isMultilineInput: function isMultilineInput(aElement) { return (aElement instanceof Ci.nsIDOMHTMLTextAreaElement); }, isLink: function isLink(aElement) { return ((aElement instanceof Ci.nsIDOMHTMLAnchorElement && aElement.href) ||
--- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js +++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js @@ -100,33 +100,35 @@ var ContextMenuHandler = { /****************************************************** * ContextCommand handlers */ _onSelectAll: function _onSelectAll() { if (Util.isTextInput(this._target)) { // select all text in the input control this._target.select(); + } else if (Util.isEditableContent(this._target)) { + this._target.ownerDocument.execCommand("selectAll", false); } else { // select the entire document content.getSelection().selectAllChildren(content.document); } this.reset(); }, _onPaste: function _onPaste() { // paste text if this is an input control if (Util.isTextInput(this._target)) { let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement); if (edit) { edit.editor.paste(Ci.nsIClipboard.kGlobalClipboard); } else { Util.dumpLn("error: target element does not support nsIDOMNSEditableElement"); } - } else if (this._target.isContentEditable) { + } else if (Util.isEditableContent(this._target)) { try { this._target.ownerDocument.execCommand("paste", false, Ci.nsIClipboard.kGlobalClipboard); } catch (ex) { dump("ContextMenuHandler: exception pasting into contentEditable: " + ex.message + "\n"); } } @@ -140,17 +142,17 @@ var ContextMenuHandler = { _onCut: function _onCut() { if (Util.isTextInput(this._target)) { let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement); if (edit) { edit.editor.cut(); } else { Util.dumpLn("error: target element does not support nsIDOMNSEditableElement"); } - } else if (this._target.isContentEditable) { + } else if (Util.isEditableContent(this._target)) { try { this._target.ownerDocument.execCommand("cut", false); } catch (ex) { dump("ContextMenuHandler: exception cutting from contentEditable: " + ex.message + "\n"); } } this.reset(); }, @@ -254,17 +256,19 @@ var ContextMenuHandler = { linkUrl = state.linkURL; state.linkTitle = popupNode.textContent || popupNode.title; state.linkProtocol = this._getProtocol(this._getURI(state.linkURL)); // mark as text so we can pickup on selection below isText = true; break; } // is the target contentEditable (not just inheriting contentEditable) - else if (elem.contentEditable == "true") { + // or the entire document in designer mode. + else if (elem.contentEditable == "true" || + Util.isOwnerDocumentInDesignMode(elem)) { this._target = elem; isEditableText = true; isText = true; uniqueStateTypes.add("input-text"); if (elem.textContent.length) { uniqueStateTypes.add("selectable"); } else {
--- a/browser/metro/base/tests/mochitest/browser_context_menu_tests.js +++ b/browser/metro/base/tests/mochitest/browser_context_menu_tests.js @@ -704,12 +704,90 @@ gTests.push({ gTests.push({ desc: "bug 856264 - touch - context menu should reopen on other links", setUp: reopenSetUp, tearDown: reopenTearDown, run: getReopenTest(sendContextMenuClickToElement, sendTap) }); +gTests.push({ + desc: "Bug 947505 - Right-click in a designMode document should display a " + + "context menu", + run: function test() { + info(chromeRoot + "browser_context_menu_tests_02.html"); + yield addTab(chromeRoot + "browser_context_menu_tests_02.html"); + + purgeEventQueue(); + emptyClipboard(); + ContextUI.dismiss(); + + yield waitForCondition(() => !ContextUI.navbarVisible); + + let tabWindow = Browser.selectedTab.browser.contentWindow; + let testSpan = tabWindow.document.getElementById("text1"); + + // Case #1: Document isn't in design mode and nothing is selected. + tabWindow.document.designMode = "off"; + + // Simulate right mouse click to reproduce the same step as noted in the + // appropriate bug. It's valid for non-touch case only. + synthesizeNativeMouseRDown(Browser.selectedTab.browser, 10, 10); + synthesizeNativeMouseRUp(Browser.selectedTab.browser, 10, 10); + + yield waitForCondition(() => ContextUI.navbarVisible); + + ok(ContextUI.navbarVisible, "Navbar is visible on context menu action."); + ok(ContextUI.tabbarVisible, "Tabbar is visible on context menu action."); + + ContextUI.dismiss(); + yield waitForCondition(() => !ContextUI.navbarVisible); + + // Case #2: Document isn't in design mode and text is selected. + tabWindow.getSelection().selectAllChildren(testSpan); + + let promise = waitForEvent(tabWindow.document, "popupshown"); + sendContextMenuClickToSelection(tabWindow); + yield promise; + + checkContextUIMenuItemVisibility(["context-copy", "context-search"]); + + promise = waitForEvent(document, "popuphidden"); + ContextMenuUI.hide(); + yield promise; + + // Case #3: Document is in design mode and nothing is selected. + tabWindow.document.designMode = "on"; + tabWindow.getSelection().removeAllRanges(); + + promise = waitForEvent(tabWindow.document, "popupshown"); + sendContextMenuClickToElement(tabWindow, testSpan); + yield promise; + + checkContextUIMenuItemVisibility(["context-select-all", "context-select"]); + + promise = waitForEvent(document, "popuphidden"); + ContextMenuUI.hide(); + yield promise; + + // Case #4: Document is in design mode and text is selected. + tabWindow.getSelection().selectAllChildren(testSpan); + + promise = waitForEvent(tabWindow.document, "popupshown"); + sendContextMenuClickToSelection(tabWindow); + yield promise; + + checkContextUIMenuItemVisibility(["context-cut", "context-copy", + "context-select-all", "context-select", + "context-search"]); + + promise = waitForEvent(document, "popuphidden"); + ContextMenuUI.hide(); + yield promise; + + Browser.closeTab(Browser.selectedTab, { forceClose: true }); + } +}); + function test() { setDevPixelEqualToPx(); runTests(); }