author | Masayuki Nakano <masayuki@d-toybox.com> |
Mon, 02 Apr 2018 17:26:46 +0900 | |
changeset 830009 | 641d0c0f8190fc7329a47450c16901621491eb20 |
parent 830008 | 0ed4fce8ca6071a2fa61245e39deb1844558a68e |
child 830010 | d21676503e6642937f54219f0e3c5a36682cd426 |
push id | 118808 |
push user | masayuki@d-toybox.com |
push date | Sat, 18 Aug 2018 08:51:19 +0000 |
reviewers | m_kato |
bugs | 1449564 |
milestone | 63.0a1 |
--- a/editor/libeditor/HTMLAnonymousNodeEditor.cpp +++ b/editor/libeditor/HTMLAnonymousNodeEditor.cpp @@ -289,19 +289,21 @@ HTMLEditor::DeleteRefToAnonymousNode(Man NS_IMETHODIMP HTMLEditor::CheckSelectionStateForAnonymousButtons(Selection* aSelection) { if (NS_WARN_IF(!aSelection)) { return NS_ERROR_INVALID_ARG; } // early way out if all contextual UI extensions are disabled - NS_ENSURE_TRUE(mIsObjectResizingEnabled || - mIsAbsolutelyPositioningEnabled || - mIsInlineTableEditingEnabled, NS_OK); + if (NS_WARN_IF(!IsObjectResizerEnabled() && + !mIsAbsolutelyPositioningEnabled && + !IsInlineTableEditorEnabled())) { + return NS_OK; + } // Don't change selection state if we're moving. if (mIsMoving) { return NS_OK; } // let's get the containing element of the selection RefPtr<Element> focusElement = GetSelectionContainerElement(*aSelection); @@ -320,24 +322,24 @@ HTMLEditor::CheckSelectionStateForAnonym RefPtr<Element> absPosElement; if (mIsAbsolutelyPositioningEnabled) { // Absolute Positioning support is enabled, is the selection contained // in an absolutely positioned element ? absPosElement = GetAbsolutelyPositionedSelectionContainer(); } RefPtr<Element> cellElement; - if (mIsObjectResizingEnabled || mIsInlineTableEditingEnabled) { + if (IsObjectResizerEnabled() || IsInlineTableEditorEnabled()) { // Resizing or Inline Table Editing is enabled, we need to check if the // selection is contained in a table cell cellElement = GetElementOrParentByTagNameAtSelection(*aSelection, *nsGkAtoms::td); } - if (mIsObjectResizingEnabled && cellElement) { + if (IsObjectResizerEnabled() && cellElement) { // we are here because Resizing is enabled AND selection is contained in // a cell // get the enclosing table if (nsGkAtoms::img != focusTagAtom) { // the element container of the selection is not an image, so we'll show // the resizers around the table focusElement = GetEnclosingTable(cellElement); @@ -360,34 +362,34 @@ HTMLEditor::CheckSelectionStateForAnonym // side effects while this code runs (bug 420439). if (mIsAbsolutelyPositioningEnabled && mAbsolutelyPositionedObject && absPosElement != mAbsolutelyPositionedObject) { HideGrabber(); NS_ASSERTION(!mAbsolutelyPositionedObject, "HideGrabber failed"); } - if (mIsObjectResizingEnabled && mResizedObject && + if (IsObjectResizerEnabled() && mResizedObject && mResizedObject != focusElement) { nsresult rv = HideResizers(); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(!mResizedObject, "HideResizers failed"); } if (mIsInlineTableEditingEnabled && mInlineEditedCell && mInlineEditedCell != cellElement) { nsresult rv = HideInlineTableEditingUI(); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUI failed"); } // now, let's display all contextual UI for good nsIContent* hostContent = GetActiveEditingHost(); - if (mIsObjectResizingEnabled && focusElement && + if (IsObjectResizerEnabled() && focusElement && IsModifiableNode(*focusElement) && focusElement != hostContent) { if (nsGkAtoms::img == focusTagAtom) { mResizedObjectIsAnImage = true; } if (mResizedObject) { nsresult rv = RefreshResizers(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
--- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -111,28 +111,28 @@ HTMLEditor::InsertNodeIntoProperAncestor const EditorRawDOMPoint& aPointToInsert, SplitAtEdges aSplitAtEdges); HTMLEditor::HTMLEditor() : mCRInParagraphCreatesParagraph(false) , mCSSAware(false) , mSelectedCellIndex(0) , mHasShownResizers(false) - , mIsObjectResizingEnabled(true) + , mIsObjectResizingEnabled(false) , mIsResizing(false) , mPreserveRatio(false) , mResizedObjectIsAnImage(false) , mIsAbsolutelyPositioningEnabled(true) , mResizedObjectIsAbsolutelyPositioned(false) , mHasShownGrabber(false) , mGrabberClicked(false) , mIsMoving(false) , mSnapToGridEnabled(false) , mHasShownInlineTableEditor(false) - , mIsInlineTableEditingEnabled(true) + , mIsInlineTableEditingEnabled(false) , mOriginalX(0) , mOriginalY(0) , mResizedObjectX(0) , mResizedObjectY(0) , mResizedObjectWidth(0) , mResizedObjectHeight(0) , mResizedObjectMarginLeft(0) , mResizedObjectMarginTop(0) @@ -3075,18 +3075,20 @@ HTMLEditor::AddOverrideStyleSheetInterna return rv; } // We MUST ONLY load synchronous local files (no @import) // XXXbz Except this will actually try to load remote files // synchronously, of course.. RefPtr<StyleSheet> sheet; // Editor override style sheets may want to style Gecko anonymous boxes - rv = presShell->GetDocument()->CSSLoader()-> - LoadSheetSync(uaURI, css::eAgentSheetFeatures, true, &sheet); + DebugOnly<nsresult> ignoredRv = + presShell->GetDocument()->CSSLoader()-> + LoadSheetSync(uaURI, css::eAgentSheetFeatures, true, &sheet); + NS_WARNING_ASSERTION(NS_SUCCEEDED(ignoredRv), "LoadSheetSync() failed"); // Synchronous loads should ALWAYS return completed if (NS_WARN_IF(!sheet)) { return NS_ERROR_FAILURE; } // Add the override style sheet // (This checks if already exists)
--- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -216,16 +216,42 @@ public: nsresult OnMouseUp(int32_t aX, int32_t aY, Element* aTarget); /** * event callback when the mouse pointer is moved * @param aMouseEvent [IN] the event */ nsresult OnMouseMove(dom::MouseEvent* aMouseEvent); + /** + * Enable/disable object resizers for <img> elements, <table> elements, + * absolute positioned elements (required absolute position editor enabled). + */ + void EnableObjectResizer(bool aEnable) + { + mIsObjectResizingEnabled = aEnable; + } + bool IsObjectResizerEnabled() const + { + return mIsObjectResizingEnabled; + } + + /** + * Enable/disable inline table editor, e.g., adding new row or column, + * removing existing row or column. + */ + void EnableInlineTableEditor(bool aEnable) + { + mIsInlineTableEditingEnabled = aEnable; + } + bool IsInlineTableEditorEnabled() const + { + return mIsInlineTableEditingEnabled; + } + // non-virtual methods of interface methods bool AbsolutePositioningEnabled() const { return mIsAbsolutelyPositioningEnabled; } /** * returns the deepest absolutely positioned container of the selection
--- a/editor/libeditor/HTMLEditorDocumentCommands.cpp +++ b/editor/libeditor/HTMLEditorDocumentCommands.cpp @@ -339,52 +339,52 @@ SetDocumentStateCommand::DoCommandParams if (NS_WARN_IF(!htmlEditor)) { return NS_ERROR_INVALID_ARG; } ErrorResult error; bool enabled = params->GetBool(STATE_ATTRIBUTE, error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } - nsresult rv = htmlEditor->SetObjectResizingEnabled(enabled); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + htmlEditor->EnableObjectResizer(enabled); return NS_OK; } if (!nsCRT::strcmp(aCommandName, "cmd_enableInlineTableEditing")) { HTMLEditor* htmlEditor = textEditor->AsHTMLEditor(); if (NS_WARN_IF(!htmlEditor)) { return NS_ERROR_INVALID_ARG; } ErrorResult error; bool enabled = params->GetBool(STATE_ATTRIBUTE, error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } - nsresult rv = htmlEditor->SetInlineTableEditingEnabled(enabled); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + htmlEditor->EnableInlineTableEditor(enabled); return NS_OK; } return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SetDocumentStateCommand::GetCommandStateParams(const char* aCommandName, nsICommandParams* aParams, nsISupports* refCon) { if (NS_WARN_IF(!aParams) || NS_WARN_IF(!refCon)) { return NS_ERROR_INVALID_ARG; } + // If the result is set to STATE_ALL as bool value, queryCommandState() + // returns the bool value. + // If the result is set to STATE_ATTRIBUTE as CString value, + // queryCommandValue() returns the string value. + // Otherwise, ignored. + // The base editor owns most state info nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon); if (NS_WARN_IF(!editor)) { return NS_ERROR_INVALID_ARG; } TextEditor* textEditor = editor->AsTextEditor(); MOZ_ASSERT(textEditor); @@ -393,65 +393,74 @@ SetDocumentStateCommand::GetCommandState // Always get the enabled state bool outCmdEnabled = false; IsCommandEnabled(aCommandName, refCon, &outCmdEnabled); nsresult rv = params->SetBool(STATE_ENABLED, outCmdEnabled); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + // cmd_setDocumentModified is an internal command. if (!nsCRT::strcmp(aCommandName, "cmd_setDocumentModified")) { bool modified; rv = textEditor->GetDocumentModified(&modified); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + // XXX Nobody refers this result due to wrong type. rv = params->SetBool(STATE_ATTRIBUTE, modified); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } + // cmd_setDocumentReadOnly is a Gecko specific command, "contentReadOnly". if (!nsCRT::strcmp(aCommandName, "cmd_setDocumentReadOnly")) { + // XXX Nobody refers this result due to wrong type. rv = params->SetBool(STATE_ATTRIBUTE, textEditor->IsReadonly()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } + // cmd_setDocumentUseCSS is a common command, "styleWithCSS". if (!nsCRT::strcmp(aCommandName, "cmd_setDocumentUseCSS")) { HTMLEditor* htmlEditor = textEditor->AsHTMLEditor(); if (NS_WARN_IF(!htmlEditor)) { return NS_ERROR_INVALID_ARG; } bool isCSS; htmlEditor->GetIsCSSEnabled(&isCSS); rv = params->SetBool(STATE_ALL, isCSS); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } + // cmd_insertBrOnReturn is a Gecko specific command, "insertBrOrReturn". if (!nsCRT::strcmp(aCommandName, "cmd_insertBrOnReturn")) { HTMLEditor* htmlEditor = textEditor->AsHTMLEditor(); if (NS_WARN_IF(!htmlEditor)) { return NS_ERROR_INVALID_ARG; } bool createPOnReturn; htmlEditor->GetReturnInParagraphCreatesNewParagraph(&createPOnReturn); + // XXX Nobody refers this result due to wrong type. rv = params->SetBool(STATE_ATTRIBUTE, !createPOnReturn); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } + // cmd_defaultParagraphSeparator is a common command, + // "defaultParagraphSeparator". if (!nsCRT::strcmp(aCommandName, "cmd_defaultParagraphSeparator")) { HTMLEditor* htmlEditor = textEditor->AsHTMLEditor(); if (NS_WARN_IF(!htmlEditor)) { return NS_ERROR_INVALID_ARG; } switch (htmlEditor->GetDefaultParagraphSeparator()) { case ParagraphSeparator::div: { @@ -476,38 +485,44 @@ SetDocumentStateCommand::GetCommandState return NS_OK; } default: MOZ_ASSERT_UNREACHABLE("Invalid paragraph separator value"); return NS_ERROR_UNEXPECTED; } } + // cmd_enableObjectResizing is a Gecko specific command, + // "enableObjectResizing". if (!nsCRT::strcmp(aCommandName, "cmd_enableObjectResizing")) { HTMLEditor* htmlEditor = textEditor->AsHTMLEditor(); if (NS_WARN_IF(!htmlEditor)) { return NS_ERROR_INVALID_ARG; } - bool enabled; - htmlEditor->GetObjectResizingEnabled(&enabled); - rv = params->SetBool(STATE_ATTRIBUTE, enabled); + // We returned the result as STATE_ATTRIBUTE with bool value 60 or earlier. + // So, the result was ignored by both nsHTMLDocument::QueryCommandValue() + // and nsHTMLDocument::QueryCommandState(). + rv = params->SetBool(STATE_ALL, htmlEditor->IsObjectResizerEnabled()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } + // cmd_enableInlineTableEditing is a Gecko specific command, + // "enableInlineTableEditing". if (!nsCRT::strcmp(aCommandName, "cmd_enableInlineTableEditing")) { HTMLEditor* htmlEditor = textEditor->AsHTMLEditor(); if (NS_WARN_IF(!htmlEditor)) { return NS_ERROR_INVALID_ARG; } - bool enabled; - htmlEditor->GetInlineTableEditingEnabled(&enabled); - rv = params->SetBool(STATE_ATTRIBUTE, enabled); + // We returned the result as STATE_ATTRIBUTE with bool value 60 or earlier. + // So, the result was ignored by both nsHTMLDocument::QueryCommandValue() + // and nsHTMLDocument::QueryCommandState(). + rv = params->SetBool(STATE_ALL, htmlEditor->IsInlineTableEditorEnabled()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } return NS_ERROR_NOT_IMPLEMENTED; }
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp +++ b/editor/libeditor/HTMLEditorObjectResizer.cpp @@ -941,20 +941,20 @@ HTMLEditor::GetResizedObject(Element** a RefPtr<Element> ret = mResizedObject; ret.forget(aResizedObject); return NS_OK; } NS_IMETHODIMP HTMLEditor::GetObjectResizingEnabled(bool* aIsObjectResizingEnabled) { - *aIsObjectResizingEnabled = mIsObjectResizingEnabled; + *aIsObjectResizingEnabled = IsObjectResizerEnabled(); return NS_OK; } NS_IMETHODIMP HTMLEditor::SetObjectResizingEnabled(bool aObjectResizingEnabled) { - mIsObjectResizingEnabled = aObjectResizingEnabled; + EnableObjectResizer(aObjectResizingEnabled); return NS_OK; } } // namespace mozilla
--- a/editor/libeditor/HTMLInlineTableEditor.cpp +++ b/editor/libeditor/HTMLInlineTableEditor.cpp @@ -23,24 +23,24 @@ namespace mozilla { // Uncomment the following line if you want to disable // table deletion when the only column/row is removed // #define DISABLE_TABLE_DELETION 1 NS_IMETHODIMP HTMLEditor::SetInlineTableEditingEnabled(bool aIsEnabled) { - mIsInlineTableEditingEnabled = aIsEnabled; + EnableInlineTableEditor(aIsEnabled); return NS_OK; } NS_IMETHODIMP HTMLEditor::GetInlineTableEditingEnabled(bool* aIsEnabled) { - *aIsEnabled = mIsInlineTableEditingEnabled; + *aIsEnabled = IsInlineTableEditorEnabled(); return NS_OK; } nsresult HTMLEditor::ShowInlineTableEditingUI(Element* aCell) { // do nothing if aCell is not a table cell... if (!aCell || !HTMLEditUtils::IsTableCell(aCell)) {
--- a/editor/libeditor/tests/mochitest.ini +++ b/editor/libeditor/tests/mochitest.ini @@ -132,18 +132,16 @@ skip-if = toolkit == 'android' #TIMED_OU skip-if = toolkit == 'android' #bug 957797 [test_bug625452.html] [test_bug629845.html] [test_bug635636.html] skip-if = os == 'android' [test_bug636465.html] skip-if = os == 'android' [test_bug638596.html] -[test_bug640321.html] -skip-if = android_version == '18' || (verify && debug && os == 'win') # bug 1147989 [test_bug641466.html] [test_bug645914.html] [test_bug646194.html] [test_bug668599.html] [test_bug674770-1.html] subsuite = clipboard skip-if = toolkit == 'android' || verify [test_bug674770-2.html] @@ -282,17 +280,19 @@ skip-if = android_version == '24' subsuite = clipboard skip-if = android_version == '24' [test_nsIHTMLEditor_getSelectedElement.html] skip-if = toolkit == 'android' && debug # bug 1480702, causes permanent failure of non-related test [test_nsIHTMLEditor_selectElement.html] skip-if = toolkit == 'android' && debug # bug 1480702, causes permanent failure of non-related test [test_nsIHTMLEditor_setCaretAfterElement.html] skip-if = toolkit == 'android' && debug # bug 1480702, causes permanent failure of non-related test -[test_objectResizing.html] +[test_resizers_appearance.html] +[test_resizers_resizing_elements.html] +skip-if = android_version == '18' || (verify && debug && os == 'win') # bug 1147989 [test_root_element_replacement.html] [test_select_all_without_body.html] [test_spellcheck_pref.html] skip-if = toolkit == 'android' [test_undo_after_spellchecker_replaces_word.html] skip-if = toolkit == 'android' [test_undo_redo_stack_after_setting_value.html] [test_backspace_vs.html]
rename from editor/libeditor/tests/test_objectResizing.html rename to editor/libeditor/tests/test_resizers_appearance.html --- a/editor/libeditor/tests/test_objectResizing.html +++ b/editor/libeditor/tests/test_resizers_appearance.html @@ -1,51 +1,95 @@ <!DOCTYPE html> <html> <head> + <title>Test for resizers appearance</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <p id="display"></p> <div id="content" style="display: none;"> </div> -<div id="editable1" contenteditable="true"> -<table id="table1"> -<tr><td>ABCDEFG</td><td>HIJKLMN</td></tr> -<tr><td>ABCDEFG</td><td>HIJKLMN</td></tr> -</table> -</div> +<div id="editor" contenteditable></div> +<div id="clickaway" style="width: 3px; height: 3px;"></div> +<img src="green.png"><!-- for ensuring to load the image at first test of <img> case --> <pre id="test"> <script class="testbody" type="application/javascript"> -SimpleTest.waitForExplicitFinish(); -SimpleTest.waitForFocus(function() { - document.execCommand("enableObjectResizing", false, "true"); +"use strict"; - let tableNode = document.getElementById("table1"); - synthesizeMouseAtCenter(tableNode, {}); - is(tableNode.getAttribute("_moz_resizing"), "true", - "_moz_resizing attribute should be true with object resizing"); +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(async function() { + async function waitForSelectionChange() { + return new Promise(resolve => { + document.addEventListener("selectionchange", () => { + resolve(); + }, {once: true}); + }); + } + + let editor = document.getElementById("editor"); + let outOfEditor = document.getElementById("clickaway"); - let originalHeight = tableNode.clientHeight; - synthesizeMouse(tableNode, 0, originalHeight, { type: "mousedown" }); - synthesizeMouse(tableNode, 0, originalHeight + 100, { type: "mousemove" }); - synthesizeMouse(tableNode, 0, originalHeight + 100, { type: "mouseup" }); - isnot(originalHeight, tableNode.clientHeight, - "table height should be changed by dragging a resizer grip"); + const kTests = [ + { description: "<img>", + innerHTML: "<img id=\"target\" src=\"green.png\" width=\"100\" height=\"100\">", + resizable: true, + }, + { description: "<table>", + innerHTML: "<table id=\"target\" border><tr><td>1-1</td><td>1-2</td></tr><tr><td>2-1</td><td>2-2</td></tr></table>", + resizable: true, + }, + { description: "absolute positioned <div>", + innerHTML: "<div id=\"target\" style=\"position: absolute; top: 50px; left: 50px;\">positioned</div>", + resizable: true, + }, + { description: "fixed positioned <div>", + innerHTML: "<div id=\"target\" style=\"position: fixed; top: 50px; left: 50px;\">positioned</div>", + resizable: false, + }, + { description: "relative positioned <div>", + innerHTML: "<div id=\"target\" style=\"position: relative; top: 50px; left: 50px;\">positioned</div>", + resizable: false, + }, + ]; - let originalWidth = tableNode.clientWidth; - synthesizeMouse(tableNode, originalWidth, 0, { type: "mousedown" }); - synthesizeMouse(tableNode, originalWidth + 100, 0, { type: "mousemove" }); - synthesizeMouse(tableNode, originalWidth + 100, 0, { type: "mouseup" }); - isnot(originalWidth, tableNode.clientWidth, - "table width should be changed by dragging a resizer grip"); + for (const kTest of kTests) { + const kDescription = kTest.description + ": "; + editor.innerHTML = kTest.innerHTML; + let target = document.getElementById("target"); + + document.execCommand("enableObjectResizing", false, false); + ok(!document.queryCommandState("enableObjectResizing"), + kDescription + "Object resizer should be disabled by the call of execCommand"); + + synthesizeMouseAtCenter(outOfEditor, {}); + let promiseSelectionChangeEvent1 = waitForSelectionChange(); + synthesizeMouseAtCenter(target, {}); + await promiseSelectionChangeEvent1; + + ok(!target.hasAttribute("_moz_resizing"), + kDescription + ": While enableObjectResizing is disabled, resizers shouldn't appear"); + + document.execCommand("enableObjectResizing", false, true); + ok(document.queryCommandState("enableObjectResizing"), + kDescription + "Object resizer should be enabled by the call of execCommand"); + + synthesizeMouseAtCenter(outOfEditor, {}); + let promiseSelectionChangeEvent2 = waitForSelectionChange(); + synthesizeMouseAtCenter(target, {}); + await promiseSelectionChangeEvent2; + + is(target.hasAttribute("_moz_resizing"), kTest.resizable, + kDescription + (kTest.resizable ? "While enableObjectResizing is enabled, resizers should appear" : + "Even while enableObjectResizing is enabled, resizers shouldn't appear")); + } SimpleTest.finish(); }); </script> </pre> </body> </html>
rename from editor/libeditor/tests/test_bug640321.html rename to editor/libeditor/tests/test_resizers_resizing_elements.html --- a/editor/libeditor/tests/test_bug640321.html +++ b/editor/libeditor/tests/test_resizers_resizing_elements.html @@ -1,190 +1,230 @@ <!DOCTYPE HTML> <html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=640321 ---> <head> - <title>Test for Bug 640321</title> + <title>Test for resizers of some elements</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <style> + #target { + background-color: green; + } + </style> </head> <body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=640321">Mozilla Bug 640321</a> <p id="display"></p> -<div id="content" contenteditable style="text-align: center"> - <img src="green.png"> -</div> +<div id="content" contenteditable style="width: 200px; height: 200px;"></div> <div id="clickaway" style="width: 10px; height: 10px"></div> +<img src="green.png"><!-- for ensuring to load the image at first test of <img> case --> <pre id="test"> <script type="application/javascript"> +"use strict"; -/** Test for Bug 640321 **/ SimpleTest.waitForExplicitFinish(); -SimpleTest.waitForFocus(function() { - var img = document.querySelector("img"); +SimpleTest.waitForFocus(async function() { + document.execCommand("enableObjectResizing", false, true); + ok(document.queryCommandState("enableObjectResizing"), + "Object resizer should be enabled by the call of execCommand"); + + let outOfEditor = document.getElementById("clickaway"); function cancel(e) { e.stopPropagation(); } - var content = document.getElementById("content"); + let content = document.getElementById("content"); content.addEventListener("mousedown", cancel); content.addEventListener("mousemove", cancel); content.addEventListener("mouseup", cancel); - /** - * This function is a generic resizer test. - * We have 8 resizers that we'd like to test, and each can be moved in 8 different directions. - * In specifying baseX, W can be considered to be the width of the image, and for baseY, H - * can be considered to be the height of the image. deltaX and deltaY are regular pixel values - * which can be positive or negative. - */ - const W = 1; - const H = 1; - function testResizer(baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY) { - ok(true, "testResizer(" + [baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY].join(", ") + ")"); - - // Reset the dimensions of the image - img.style.width = "100px"; - img.style.height = "100px"; - var rect = img.getBoundingClientRect(); - is(rect.width, 100, "Sanity check the length"); - is(rect.height, 100, "Sanity check the height"); - - // Click on the image to show the resizers - synthesizeMouseAtCenter(img, {}); - - // Determine which resizer we're dealing with - var basePosX = rect.width * baseX; - var basePosY = rect.height * baseY; - - // Click on the correct resizer - synthesizeMouse(img, basePosX, basePosY, {type: "mousedown"}); - // Drag it delta pixels to the right and bottom (or maybe left and top!) - synthesizeMouse(img, basePosX + deltaX, basePosY + deltaY, {type: "mousemove"}); - // Release the mouse button - synthesizeMouse(img, basePosX + deltaX, basePosY + deltaY, {type: "mouseup"}); - // Move the mouse delta more pixels to the same direction to make sure that the - // resize operation has stopped. - synthesizeMouse(img, basePosX + deltaX * 2, basePosY + deltaY * 2, {type: "mousemove"}); - // Click outside of the image to hide the resizers - synthesizeMouseAtCenter(document.getElementById("clickaway"), {}); - - // Get the new dimensions for the image - var newRect = img.getBoundingClientRect(); - is(newRect.width, rect.width + expectedDeltaX, "The width should be increased by " + expectedDeltaX + " pixels"); - is(newRect.height, rect.height + expectedDeltaY, "The height should be increased by " + expectedDeltaY + "pixels"); + async function waitForSelectionChange() { + return new Promise(resolve => { + document.addEventListener("selectionchange", () => { + resolve(); + }, {once: true}); + }); } - function runTests(preserveRatio) { + async function doTest(aDescription, aPreserveRatio, aInnerHTML) { + let description = aDescription; + if (SpecialPowers.getBoolPref("editor.resizing.preserve_ratio")) { + description += " (preserve ratio pref is true)"; + } + description += ": "; + content.innerHTML = aInnerHTML; + let target = document.getElementById("target"); + + /** + * This function is a generic resizer test. + * We have 8 resizers that we'd like to test, and each can be moved in 8 different directions. + * In specifying baseX, W can be considered to be the width of the image, and for baseY, H + * can be considered to be the height of the image. deltaX and deltaY are regular pixel values + * which can be positive or negative. + */ + const W = 1; + const H = 1; + async function testResizer(baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY) { + ok(true, description + "testResizer(" + [baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY].join(", ") + ")"); + + // Reset the dimensions of the target. + target.style.width = "150px"; + target.style.height = "150px"; + let rect = target.getBoundingClientRect(); + is(rect.width, 150, description + "Sanity check the width"); + is(rect.height, 150, description + "Sanity check the height"); + + // Click on the target to show the resizers + let promiseSelectionChangeEvent = waitForSelectionChange(); + synthesizeMouseAtCenter(target, {}); + await promiseSelectionChangeEvent; + + // Determine which resizer we're dealing with. + let basePosX = rect.width * baseX; + let basePosY = rect.height * baseY; + + // Click on the correct resizer + synthesizeMouse(target, basePosX, basePosY, {type: "mousedown"}); + // Drag it delta pixels to the right and bottom (or maybe left and top!) + synthesizeMouse(target, basePosX + deltaX, basePosY + deltaY, {type: "mousemove"}); + // Release the mouse button + synthesizeMouse(target, basePosX + deltaX, basePosY + deltaY, {type: "mouseup"}); + // Move the mouse delta more pixels to the same direction to make sure that the + // resize operation has stopped. + synthesizeMouse(target, basePosX + deltaX * 2, basePosY + deltaY * 2, {type: "mousemove"}); + // Click outside of the editor to hide the resizers + synthesizeMouseAtCenter(outOfEditor, {}); + + // Get the new dimensions for the target + let newRect = target.getBoundingClientRect(); + isfuzzy(newRect.width, rect.width + expectedDeltaX, 1, description + "The width should be increased by " + expectedDeltaX + " pixels"); + isfuzzy(newRect.height, rect.height + expectedDeltaY, 1, description + "The height should be increased by " + expectedDeltaY + "pixels"); + } + // Account for changes in the resizing behavior when we're trying to preserve - // the aspect ration. + // the aspect ration of image. // ignoredGrowth means we don't change the size of a dimension because otherwise // the aspect ratio would change undesirably. // needlessGrowth means that we change the size of a dimension perpendecular to // the mouse movement axis in order to preserve the aspect ratio. // reversedGrowth means that we change the size of a dimension in the opposite // direction to the mouse movement in order to maintain the aspect ratio. - const ignoredGrowth = preserveRatio ? 0 : 1; - const needlessGrowth = preserveRatio ? 1 : 0; - const reversedGrowth = preserveRatio ? -1 : 1; + const ignoredGrowth = aPreserveRatio ? 0 : 1; + const needlessGrowth = aPreserveRatio ? 1 : 0; + const reversedGrowth = aPreserveRatio ? -1 : 1; // top resizer - testResizer(W/2, 0, -10, -10, 0, 10); - testResizer(W/2, 0, -10, 0, 0, 0); - testResizer(W/2, 0, -10, 10, 0, -10); - testResizer(W/2, 0, 0, -10, 0, 10); - testResizer(W/2, 0, 0, 0, 0, 0); - testResizer(W/2, 0, 0, 10, 0, -10); - testResizer(W/2, 0, 10, -10, 0, 10); - testResizer(W/2, 0, 10, 0, 0, 0); - testResizer(W/2, 0, 10, 10, 0, -10); + await testResizer(W/2, 0, -10, -10, 0, 10); + await testResizer(W/2, 0, -10, 0, 0, 0); + await testResizer(W/2, 0, -10, 10, 0, -10); + await testResizer(W/2, 0, 0, -10, 0, 10); + await testResizer(W/2, 0, 0, 0, 0, 0); + await testResizer(W/2, 0, 0, 10, 0, -10); + await testResizer(W/2, 0, 10, -10, 0, 10); + await testResizer(W/2, 0, 10, 0, 0, 0); + await testResizer(W/2, 0, 10, 10, 0, -10); // top right resizer - testResizer( W, 0, -10, -10, -10 * reversedGrowth, 10); - testResizer( W, 0, -10, 0, -10 * ignoredGrowth, 0); - testResizer( W, 0, -10, 10, -10, -10); - testResizer( W, 0, 0, -10, 10 * needlessGrowth, 10); - testResizer( W, 0, 0, 0, 0, 0); - testResizer( W, 0, 0, 10, 0, -10 * ignoredGrowth); - testResizer( W, 0, 10, -10, 10, 10); - testResizer( W, 0, 10, 0, 10, 10 * needlessGrowth); - testResizer( W, 0, 10, 10, 10, -10 * reversedGrowth); + await testResizer( W, 0, -10, -10, -10 * reversedGrowth, 10); + await testResizer( W, 0, -10, 0, -10 * ignoredGrowth, 0); + await testResizer( W, 0, -10, 10, -10, -10); + await testResizer( W, 0, 0, -10, 10 * needlessGrowth, 10); + await testResizer( W, 0, 0, 0, 0, 0); + await testResizer( W, 0, 0, 10, 0, -10 * ignoredGrowth); + await testResizer( W, 0, 10, -10, 10, 10); + await testResizer( W, 0, 10, 0, 10, 10 * needlessGrowth); + await testResizer( W, 0, 10, 10, 10, -10 * reversedGrowth); // right resizer - testResizer( W, H/2, -10, -10, -10, 0); - testResizer( W, H/2, -10, 0, -10, 0); - testResizer( W, H/2, -10, 10, -10, 0); - testResizer( W, H/2, 0, -10, 0, 0); - testResizer( W, H/2, 0, 0, 0, 0); - testResizer( W, H/2, 0, 10, 0, 0); - testResizer( W, H/2, 10, -10, 10, 0); - testResizer( W, H/2, 10, 0, 10, 0); - testResizer( W, H/2, 10, 10, 10, 0); + await testResizer( W, H/2, -10, -10, -10, 0); + await testResizer( W, H/2, -10, 0, -10, 0); + await testResizer( W, H/2, -10, 10, -10, 0); + await testResizer( W, H/2, 0, -10, 0, 0); + await testResizer( W, H/2, 0, 0, 0, 0); + await testResizer( W, H/2, 0, 10, 0, 0); + await testResizer( W, H/2, 10, -10, 10, 0); + await testResizer( W, H/2, 10, 0, 10, 0); + await testResizer( W, H/2, 10, 10, 10, 0); // bottom right resizer - testResizer( W, H, -10, -10, -10, -10); - testResizer( W, H, -10, 0, -10 * ignoredGrowth, 0); - testResizer( W, H, -10, 10, -10 * reversedGrowth, 10); - testResizer( W, H, 0, -10, 0, -10 * ignoredGrowth); - testResizer( W, H, 0, 0, 0, 0); - testResizer( W, H, 0, 10, 10 * needlessGrowth, 10); - testResizer( W, H, 10, -10, 10, -10 * reversedGrowth); - testResizer( W, H, 10, 0, 10, 10 * needlessGrowth); - testResizer( W, H, 10, 10, 10, 10); + await testResizer( W, H, -10, -10, -10, -10); + await testResizer( W, H, -10, 0, -10 * ignoredGrowth, 0); + await testResizer( W, H, -10, 10, -10 * reversedGrowth, 10); + await testResizer( W, H, 0, -10, 0, -10 * ignoredGrowth); + await testResizer( W, H, 0, 0, 0, 0); + await testResizer( W, H, 0, 10, 10 * needlessGrowth, 10); + await testResizer( W, H, 10, -10, 10, -10 * reversedGrowth); + await testResizer( W, H, 10, 0, 10, 10 * needlessGrowth); + await testResizer( W, H, 10, 10, 10, 10); // bottom resizer - testResizer(W/2, H, -10, -10, 0, -10); - testResizer(W/2, H, -10, 0, 0, 0); - testResizer(W/2, H, -10, 10, 0, 10); - testResizer(W/2, H, 0, -10, 0, -10); - testResizer(W/2, H, 0, 0, 0, 0); - testResizer(W/2, H, 0, 10, 0, 10); - testResizer(W/2, H, 10, -10, 0, -10); - testResizer(W/2, H, 10, 0, 0, 0); - testResizer(W/2, H, 10, 10, 0, 10); + await testResizer(W/2, H, -10, -10, 0, -10); + await testResizer(W/2, H, -10, 0, 0, 0); + await testResizer(W/2, H, -10, 10, 0, 10); + await testResizer(W/2, H, 0, -10, 0, -10); + await testResizer(W/2, H, 0, 0, 0, 0); + await testResizer(W/2, H, 0, 10, 0, 10); + await testResizer(W/2, H, 10, -10, 0, -10); + await testResizer(W/2, H, 10, 0, 0, 0); + await testResizer(W/2, H, 10, 10, 0, 10); // bottom left resizer - testResizer( 0, H, -10, -10, 10, -10 * reversedGrowth); - testResizer( 0, H, -10, 0, 10, 10 * needlessGrowth); - testResizer( 0, H, -10, 10, 10, 10); - testResizer( 0, H, 0, -10, 0, -10 * ignoredGrowth); - testResizer( 0, H, 0, 0, 0, 0); - testResizer( 0, H, 0, 10, 10 * needlessGrowth, 10); - testResizer( 0, H, 10, -10, -10, -10); - testResizer( 0, H, 10, 0, -10 * ignoredGrowth, 0); - testResizer( 0, H, 10, 10, -10 * reversedGrowth, 10); + await testResizer( 0, H, -10, -10, 10, -10 * reversedGrowth); + await testResizer( 0, H, -10, 0, 10, 10 * needlessGrowth); + await testResizer( 0, H, -10, 10, 10, 10); + await testResizer( 0, H, 0, -10, 0, -10 * ignoredGrowth); + await testResizer( 0, H, 0, 0, 0, 0); + await testResizer( 0, H, 0, 10, 10 * needlessGrowth, 10); + await testResizer( 0, H, 10, -10, -10, -10); + await testResizer( 0, H, 10, 0, -10 * ignoredGrowth, 0); + await testResizer( 0, H, 10, 10, -10 * reversedGrowth, 10); // left resizer - testResizer( 0, H/2, -10, -10, 10, 0); - testResizer( 0, H/2, -10, 0, 10, 0); - testResizer( 0, H/2, -10, 10, 10, 0); - testResizer( 0, H/2, 0, -10, 0, 0); - testResizer( 0, H/2, 0, 0, 0, 0); - testResizer( 0, H/2, 0, 10, 0, 0); - testResizer( 0, H/2, 10, -10, -10, 0); - testResizer( 0, H/2, 10, 0, -10, 0); - testResizer( 0, H/2, 10, 10, -10, 0); + await testResizer( 0, H/2, -10, -10, 10, 0); + await testResizer( 0, H/2, -10, 0, 10, 0); + await testResizer( 0, H/2, -10, 10, 10, 0); + await testResizer( 0, H/2, 0, -10, 0, 0); + await testResizer( 0, H/2, 0, 0, 0, 0); + await testResizer( 0, H/2, 0, 10, 0, 0); + await testResizer( 0, H/2, 10, -10, -10, 0); + await testResizer( 0, H/2, 10, 0, -10, 0); + await testResizer( 0, H/2, 10, 10, -10, 0); // top left resizer - testResizer( 0, 0, -10, -10, 10, 10); - testResizer( 0, 0, -10, 0, 10, 10 * needlessGrowth); - testResizer( 0, 0, -10, 10, 10, -10 * reversedGrowth); - testResizer( 0, 0, 0, -10, 10 * needlessGrowth, 10); - testResizer( 0, 0, 0, 0, 0, 0); - testResizer( 0, 0, 0, 10, 0, -10 * ignoredGrowth); - testResizer( 0, 0, 10, -10, -10 * reversedGrowth, 10); - testResizer( 0, 0, 10, 0, -10 * ignoredGrowth, 0); - testResizer( 0, 0, 10, 10, -10, -10); - } - SpecialPowers.pushPrefEnv({"set": [["editor.resizing.preserve_ratio", false]]}, function() { - runTests(false); - SpecialPowers.pushPrefEnv({"set": [["editor.resizing.preserve_ratio", true]]}, function() { - runTests(true); - SimpleTest.finish(); - }); - }); - }); + await testResizer( 0, 0, -10, -10, 10, 10); + await testResizer( 0, 0, -10, 0, 10, 10 * needlessGrowth); + await testResizer( 0, 0, -10, 10, 10, -10 * reversedGrowth); + await testResizer( 0, 0, 0, -10, 10 * needlessGrowth, 10); + await testResizer( 0, 0, 0, 0, 0, 0); + await testResizer( 0, 0, 0, 10, 0, -10 * ignoredGrowth); + await testResizer( 0, 0, 10, -10, -10 * reversedGrowth, 10); + await testResizer( 0, 0, 10, 0, -10 * ignoredGrowth, 0); + await testResizer( 0, 0, 10, 10, -10, -10); + } + const kTests = [ + { description: "Resiziers for <img>", + innerHTML: "<img id=\"target\" src=\"green.png\">", + mayPreserveRatio: true, + }, + { description: "Resiziers for <table>", + innerHTML: "<table id=\"target\" border><tr><td>cell</td><td>cell</td></tr></table>", + mayPreserveRatio: false, + }, + { description: "Resiziers for absolute positioned <div>", + innerHTML: "<div id=\"target\" style=\"position: absolute; top: 50px; left: 50px;\">positioned</div>", + mayPreserveRatio: false, + }, + ]; + + await SpecialPowers.pushPrefEnv({"set": [["editor.resizing.preserve_ratio", false]]}); + for (const kTest of kTests) { + await doTest(kTest.description, false, kTest.innerHTML); + } + await SpecialPowers.pushPrefEnv({"set": [["editor.resizing.preserve_ratio", true]]}); + for (const kTest of kTests) { + await doTest(kTest.description, kTest.mayPreserveRatio, kTest.innerHTML); + } + content.innerHTML = ""; + SimpleTest.finish(); +}); </script> </pre> </body> </html>
--- a/layout/base/tests/bug558663.html +++ b/layout/base/tests/bug558663.html @@ -30,17 +30,19 @@ function checkSnapshots(s1, s2, shouldBe ok(true, testName + " snapshots compare correctly"); } else { ok(false, testName + " snapshots compare incorrectly. snapshot 1: " + res[1] + " snapshot 2: " + res[2]); } } function runTest() { - document.getElementById("iframe").contentWindow.document.designMode = "on"; + var contentDocument = document.getElementById("iframe").contentDocument; + contentDocument.designMode = "on"; + contentDocument.execCommand("enableObjectResizing", false, true); // The editor requires the event loop to spin after you turn on design mode // before it takes effect. setTimeout(continueTest, 100); } function continueTest() { var win = document.getElementById("iframe").contentWindow;
--- a/layout/generic/test/test_image_selection.html +++ b/layout/generic/test/test_image_selection.html @@ -68,24 +68,35 @@ function step3() { gImage.src = src; } function step4() { gImage.removeEventListener("load", step4); gFuchsiaSelected = snapshotWindow(gIframe.contentWindow, false); - assert_different(gBlueNotSelected, gBlueSelected, - "selecting image should add drag points"); + if (gIframe.contentDocument.queryCommandState("enableObjectResizing")) { + assert_different(gBlueNotSelected, gBlueSelected, + "selecting image should add drag points"); + } else { + assert_equal(gBlueNotSelected, gBlueSelected, + "selecting image should not change anything visually"); + } assert_different(gBlueSelected, gFuchsiaSelected, "different images should appear different"); SimpleTest.finish(); } +function assert_equal(shot1, shot2, desc) +{ + var [correct, s1, s2] = compareSnapshots(shot1, shot2, true); + ok(correct, desc + (correct ? "" : "\nRESULT: " + s2)); +} + function assert_different(shot1, shot2, desc) { var [correct, s1, s2] = compareSnapshots(shot1, shot2, false); ok(correct, desc); } </script> </pre>