diff --git a/browser/components/aboutlogins/content/components/fxaccounts-button.css b/browser/components/aboutlogins/content/components/fxaccounts-button.css
index 139bf974cd577..03a0a455b24bf 100644
--- a/browser/components/aboutlogins/content/components/fxaccounts-button.css
+++ b/browser/components/aboutlogins/content/components/fxaccounts-button.css
@@ -28,7 +28,6 @@
}
.fxaccount-email {
- font-weight: 600;
font-size: .9em;
vertical-align: middle;
}
diff --git a/browser/components/aboutlogins/content/components/login-item.css b/browser/components/aboutlogins/content/components/login-item.css
index 8a01debd3c110..d8f35bf012135 100644
--- a/browser/components/aboutlogins/content/components/login-item.css
+++ b/browser/components/aboutlogins/content/components/login-item.css
@@ -148,7 +148,7 @@ input[type="url"][readOnly]:hover:active {
display: block;
font-size: smaller;
color: var(--in-content-deemphasized-text);
- margin-bottom: 5px;
+ margin-bottom: 8px;
}
:host([data-editing]) .detail-cell input:not([readOnly]):not([type="checkbox"]) {
diff --git a/browser/components/aboutlogins/content/components/login-item.js b/browser/components/aboutlogins/content/components/login-item.js
index 556dc2c37d372..95d87926a8c91 100644
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -146,7 +146,8 @@ export default class LoginItem extends HTMLElement {
this._title.textContent = this._login.title;
this._originInput.defaultValue = this._login.origin || "";
this._usernameInput.defaultValue = this._login.username || "";
- this._passwordInput.defaultValue = this._login.password || "";
+ // The password gets filled in _updatePasswordRevealState
+
if (this.dataset.editing) {
this._usernameInput.removeAttribute("data-l10n-id");
this._usernameInput.placeholder = "";
@@ -246,7 +247,8 @@ export default class LoginItem extends HTMLElement {
case "click": {
let classList = event.currentTarget.classList;
if (classList.contains("reveal-password-checkbox")) {
- if (this._revealCheckbox.checked) {
+ // We prompt for the master password when entering edit mode already.
+ if (this._revealCheckbox.checked && !this.dataset.editing) {
let masterPasswordAuth = await new Promise(resolve => {
window.AboutLoginsUtils.promptForMasterPassword(resolve);
});
@@ -344,6 +346,13 @@ export default class LoginItem extends HTMLElement {
return;
}
if (classList.contains("edit-button")) {
+ let masterPasswordAuth = await new Promise(resolve => {
+ window.AboutLoginsUtils.promptForMasterPassword(resolve);
+ });
+ if (!masterPasswordAuth) {
+ return;
+ }
+
this._toggleEditing();
this.render();
@@ -680,7 +689,15 @@ export default class LoginItem extends HTMLElement {
let { checked } = this._revealCheckbox;
let inputType = checked ? "text" : "password";
- this._passwordInput.setAttribute("type", inputType);
+ this._passwordInput.type = inputType;
+ // Don't include the password value in the attribute when it's supposed to be
+ // masked so that it's not trivial to bypass the Master Password prompt with
+ // the inspector in devtools.
+ let password = this._login.password || "";
+ // We prompt for the master password before entering edit mode so we can use
+ // the password in the markup then.
+ this._passwordInput.defaultValue =
+ checked || this.dataset.editing ? password : " ".repeat(password.length);
}
}
customElements.define("login-item", LoginItem);
diff --git a/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js b/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js
index ce4e72137cb9f..9c7e80ccfe16c 100644
--- a/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js
+++ b/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js
@@ -79,7 +79,7 @@ add_task(async function test_telemetry_events() {
let promiseNewTab = BrowserTestUtils.waitForNewTab(
gBrowser,
- TEST_LOGIN2.origin
+ TEST_LOGIN2.origin + "/"
);
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
let loginItem = content.document.querySelector("login-item");
diff --git a/browser/components/aboutlogins/tests/browser/browser_openSite.js b/browser/components/aboutlogins/tests/browser/browser_openSite.js
index 8c8aa45378ed7..a747be387c05b 100644
--- a/browser/components/aboutlogins/tests/browser/browser_openSite.js
+++ b/browser/components/aboutlogins/tests/browser/browser_openSite.js
@@ -16,7 +16,7 @@ add_task(async function setup() {
add_task(async function test_launch_login_item() {
let promiseNewTab = BrowserTestUtils.waitForNewTab(
gBrowser,
- TEST_LOGIN1.origin
+ TEST_LOGIN1.origin + "/"
);
let browser = gBrowser.selectedBrowser;
diff --git a/browser/components/aboutlogins/tests/browser/browser_updateLogin.js b/browser/components/aboutlogins/tests/browser/browser_updateLogin.js
index 8c002093d79f6..8298a8357d827 100644
--- a/browser/components/aboutlogins/tests/browser/browser_updateLogin.js
+++ b/browser/components/aboutlogins/tests/browser/browser_updateLogin.js
@@ -63,6 +63,10 @@ add_task(async function test_login_item() {
async function test_discard_dialog(exitPoint) {
editButton.click();
+ await ContentTaskUtils.waitForCondition(
+ () => loginItem.dataset.editing,
+ "Entering edit mode"
+ );
await Promise.resolve();
usernameInput.value += "-undome";
@@ -102,7 +106,7 @@ add_task(async function test_login_item() {
);
is(
passwordInput.value,
- login.password,
+ " ".repeat(login.password.length),
"Password change should be reverted"
);
is(
@@ -118,6 +122,10 @@ add_task(async function test_login_item() {
await test_discard_dialog(cancelButton);
editButton.click();
+ await ContentTaskUtils.waitForCondition(
+ () => loginItem.dataset.editing,
+ "Entering edit mode"
+ );
await Promise.resolve();
let revealCheckbox = loginItem.shadowRoot.querySelector(
@@ -132,6 +140,8 @@ add_task(async function test_login_item() {
usernameInput.value += "-saveme";
passwordInput.value += "-saveme";
+ // Cache the value since it will change upon leaving edit mode.
+ let passwordInputValue = passwordInput.value;
ok(loginItem.dataset.editing, "LoginItem should be in 'edit' mode");
let saveChangesButton = loginItem.shadowRoot.querySelector(
@@ -145,7 +155,7 @@ add_task(async function test_login_item() {
return (
updatedLogin &&
updatedLogin.username == usernameInput.value &&
- updatedLogin.password == passwordInput.value
+ updatedLogin.password == passwordInputValue
);
}, "Waiting for corresponding login in login list to update");
@@ -164,6 +174,10 @@ add_task(async function test_login_item() {
);
editButton.click();
+ await ContentTaskUtils.waitForCondition(
+ () => loginItem.dataset.editing,
+ "Entering edit mode"
+ );
await Promise.resolve();
ok(loginItem.dataset.editing, "LoginItem should be in 'edit' mode");
diff --git a/browser/components/aboutlogins/tests/browser/head.js b/browser/components/aboutlogins/tests/browser/head.js
index c1e5485298402..26d97d990ffb8 100644
--- a/browser/components/aboutlogins/tests/browser/head.js
+++ b/browser/components/aboutlogins/tests/browser/head.js
@@ -8,8 +8,8 @@ let nsLoginInfo = new Components.Constructor(
);
let TEST_LOGIN1 = new nsLoginInfo(
- "https://example.com/",
- "https://example.com/",
+ "https://example.com",
+ "https://example.com",
null,
"user1",
"pass1",
@@ -17,8 +17,8 @@ let TEST_LOGIN1 = new nsLoginInfo(
"password"
);
let TEST_LOGIN2 = new nsLoginInfo(
- "https://2.example.com/",
- "https://2.example.com/",
+ "https://2.example.com",
+ "https://2.example.com",
null,
"user2",
"pass2",
@@ -27,8 +27,8 @@ let TEST_LOGIN2 = new nsLoginInfo(
);
let TEST_LOGIN3 = new nsLoginInfo(
- "https://breached.com/",
- "https://breached.com/",
+ "https://breached.com",
+ "https://breached.com",
null,
"breachedLogin1",
"pass3",
diff --git a/browser/components/aboutlogins/tests/chrome/test_login_item.html b/browser/components/aboutlogins/tests/chrome/test_login_item.html
index 10cdc42b1018b..131c543a382b8 100644
--- a/browser/components/aboutlogins/tests/chrome/test_login_item.html
+++ b/browser/components/aboutlogins/tests/chrome/test_login_item.html
@@ -89,7 +89,7 @@
let usernameInput = gLoginItem.shadowRoot.querySelector("input[name='username']");
is(usernameInput.value, TEST_LOGIN_1.username, "username should be populated");
is(document.l10n.getAttributes(usernameInput).id, "about-logins-login-item-username", "username field should have default placeholder when not editing");
- is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be populated");
+ is(gLoginItem.shadowRoot.querySelector("input[name='password']").value.length, TEST_LOGIN_1.password.length, "password mask text should be populated");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-created")).args.timeCreated, TEST_LOGIN_1.timeCreated, "time-created should be populated");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-changed")).args.timeChanged, TEST_LOGIN_1.timePasswordChanged, "time-changed should be populated");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-used")).args.timeUsed, TEST_LOGIN_1.timeLastUsed, "time-used should be populated");
@@ -126,6 +126,7 @@
usernameInput.placeholder = "dummy placeholder";
gLoginItem.shadowRoot.querySelector(".edit-button").click();
+ await asyncElementRendered();
is(
document.l10n.getAttributes(usernameInput).id,
null,
@@ -160,6 +161,7 @@
usernameInput.placeholder = "dummy placeholder";
gLoginItem.shadowRoot.querySelector(".edit-button").click();
await asyncElementRendered();
+ await asyncElementRendered();
ok(gLoginItem.dataset.editing, "loginItem should be in 'edit' mode");
ok(isHidden(gLoginItem.shadowRoot.querySelector(".edit-button")), "edit button should be hidden in 'edit' mode");
@@ -199,6 +201,7 @@
add_task(async function test_edit_login_cancel() {
gLoginItem.setLogin(TEST_LOGIN_1);
gLoginItem.shadowRoot.querySelector(".edit-button").click();
+ await asyncElementRendered();
ok(gLoginItem.dataset.editing, "loginItem should be in 'edit' mode");
is(!!gLoginItem.dataset.isNewLogin, false,
@@ -231,6 +234,8 @@
let editButton = gLoginItem.shadowRoot.querySelector(".edit-button");
editButton.click();
+ await asyncElementRendered();
+
ok(revealCheckbox.checked, "reveal-checkbox should remain checked when entering 'edit' mode");
gLoginItem.shadowRoot.querySelector(".cancel-button").click();
ok(!revealCheckbox.checked, "reveal-checkbox should be unchecked after canceling 'edit' mode");
@@ -307,7 +312,7 @@
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, TEST_LOGIN_1.origin, "origin should be unchanged");
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
- is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
+ is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, " ".repeat(TEST_LOGIN_1.password.length), "password length should be unchanged");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-created")).args.timeCreated, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-changed")).args.timeChanged, TEST_LOGIN_1.timePasswordChanged, "time-changed should be unchanged");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-used")).args.timeUsed, TEST_LOGIN_1.timeLastUsed, "time-used should be unchanged");
@@ -321,7 +326,7 @@
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, TEST_LOGIN_1.origin, "origin should be unchanged");
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
- is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
+ is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, " ".repeat(TEST_LOGIN_1.password.length), "password length should be unchanged");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-created")).args.timeCreated, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-changed")).args.timeChanged, TEST_LOGIN_1.timePasswordChanged, "time-changed should be unchanged");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-used")).args.timeUsed, TEST_LOGIN_1.timeLastUsed, "time-used should be unchanged");
@@ -335,7 +340,7 @@
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, modifiedLogin.origin, "origin should be updated");
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, modifiedLogin.username, "username should be updated");
- is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, modifiedLogin.password, "password should be updated");
+ is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, " ".repeat(modifiedLogin.password.length), "password length should be updated");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-created")).args.timeCreated, modifiedLogin.timeCreated, "time-created should be updated");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-changed")).args.timeChanged, modifiedLogin.timePasswordChanged, "time-changed should be updated");
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-used")).args.timeUsed, modifiedLogin.timeLastUsed, "time-used should be updated");
diff --git a/build/unix/elfhack/elfhack.cpp b/build/unix/elfhack/elfhack.cpp
index 2c24cbac4b577..ec01e54674b28 100644
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -194,7 +194,7 @@ class ElfRelHackCode_Section : public ElfSection {
~ElfRelHackCode_Section() { delete elf; }
- void serialize(std::ofstream& file, char ei_class, char ei_data) {
+ void serialize(std::ofstream& file, char ei_class, char ei_data) override {
// Readjust code offsets
for (std::vector::iterator c = code.begin(); c != code.end();
++c)
@@ -217,7 +217,7 @@ class ElfRelHackCode_Section : public ElfSection {
ElfSection::serialize(file, ei_class, ei_data);
}
- bool isRelocatable() { return false; }
+ bool isRelocatable() override { return false; }
unsigned int getEntryPoint() { return entry_point; }
diff --git a/config/recurse.mk b/config/recurse.mk
index 91cbc2a1d68c9..7e979e47bf59f 100644
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -203,7 +203,9 @@ $(addprefix build/unix/stdc++compat/,target host) build/clang-plugin/host: confi
# export, which ensures it exists before recursing the rust targets, tricking
# Make into keeping them early.
$(rust_targets): $(DEPTH)/.cargo/config
+ifndef TEST_MOZBUILD
export:: $(DEPTH)/.cargo/config
+endif
# When building gtest as part of the build (LINK_GTEST_DURING_COMPILE),
# force the build system to get to it first, so that it can be linked
diff --git a/devtools/client/debugger/src/utils/utils.js b/devtools/client/debugger/src/utils/utils.js
index b188c8a67b451..c4a9a023cbf06 100644
--- a/devtools/client/debugger/src/utils/utils.js
+++ b/devtools/client/debugger/src/utils/utils.js
@@ -5,7 +5,7 @@
// @flow
import type { SourceContent } from "../types";
-import { saveAs } from "devtools-modules";
+import { DevToolsUtils } from "devtools-modules";
/**
* Utils for utils, by utils
@@ -62,5 +62,5 @@ export function downloadFile(content: SourceContent, fileName: string) {
}
const data = new TextEncoder().encode(content.value);
- saveAs(window, data, fileName);
+ DevToolsUtils.saveAs(window, data, fileName);
}
diff --git a/devtools/client/shared/build/build-debugger.js b/devtools/client/shared/build/build-debugger.js
index 78af39fd7e24a..a96dbb0e8ae34 100644
--- a/devtools/client/shared/build/build-debugger.js
+++ b/devtools/client/shared/build/build-debugger.js
@@ -72,6 +72,7 @@ const moduleMapping = {
asyncStoreHelper: "devtools/client/shared/async-store-helper",
asyncStorage: "devtools/shared/async-storage",
PluralForm: "devtools/shared/plural-form",
+ DevToolsUtils: "devtools/shared/DevToolsUtils",
};
/*
diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp
index f359e340a854e..676fe56600d95 100644
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -223,6 +223,10 @@ template Element* HTMLEditor::GetInvisibleBRElementAt(
const EditorDOMPoint& aPoint);
template Element* HTMLEditor::GetInvisibleBRElementAt(
const EditorRawDOMPoint& aPoint);
+template nsIContent* HTMLEditor::FindNearEditableContent(
+ const EditorDOMPoint& aPoint, nsIEditor::EDirection aDirection);
+template nsIContent* HTMLEditor::FindNearEditableContent(
+ const EditorRawDOMPoint& aPoint, nsIEditor::EDirection aDirection);
HTMLEditRules::HTMLEditRules() : mHTMLEditor(nullptr), mInitialized(false) {
mIsHTMLEditRules = true;
@@ -361,10 +365,14 @@ nsresult HTMLEditRules::BeforeEdit() {
}
// Check that selection is in subtree defined by body node
- nsresult rv = ConfirmSelectionInBody();
+ nsresult rv =
+ MOZ_KnownLive(HTMLEditorRef()).EnsureSelectionInBodyOrDocumentElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "EnsureSelectionInBodyOrDocumentElement() failed, but ignored");
return NS_OK;
}
@@ -415,11 +423,14 @@ nsresult HTMLEditRules::AfterEdit() {
nsresult HTMLEditRules::AfterEditInner() {
MOZ_ASSERT(IsEditorDataAvailable());
- nsresult rv = ConfirmSelectionInBody();
+ nsresult rv =
+ MOZ_KnownLive(HTMLEditorRef()).EnsureSelectionInBodyOrDocumentElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to normalize Selection");
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "EnsureSelectionInBodyOrDocumentElement() failed, but ignored");
switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
case EditSubAction::eReplaceHeadWithHTMLSource:
case EditSubAction::eCreatePaddingBRElementForEmptyEditor:
@@ -614,7 +625,8 @@ nsresult HTMLEditRules::AfterEditInner() {
// did it.
if (!HTMLEditorRef()
.TopLevelEditSubActionDataRef()
- .mDidDeleteEmptyParentBlocks) {
+ .mDidDeleteEmptyParentBlocks &&
+ SelectionRefPtr()->IsCollapsed()) {
switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
@@ -623,8 +635,13 @@ nsresult HTMLEditRules::AfterEditInner() {
case EditSubAction::eInsertParagraphSeparator:
case EditSubAction::ePasteHTMLContent:
case EditSubAction::eInsertHTMLSource:
- rv = AdjustSelection(
- HTMLEditorRef().GetDirectionOfTopLevelEditSubAction());
+ // XXX AdjustCaretPositionAndEnsurePaddingBRElement() intentionally
+ // does not create padding ` ` element for empty editor.
+ // Investigate which is better that whether this should does it
+ // or wait MaybeCreatePaddingBRElementForEmptyEditor().
+ rv = MOZ_KnownLive(HTMLEditorRef())
+ .AdjustCaretPositionAndEnsurePaddingBRElement(
+ HTMLEditorRef().GetDirectionOfTopLevelEditSubAction());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -788,8 +805,6 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
return WillAbsolutePosition(aCancel, aHandled);
case EditSubAction::eSetPositionToStatic:
return WillRemoveAbsolutePosition(aCancel, aHandled);
- case EditSubAction::eSetOrClearAlignment:
- return WillAlign(*aInfo.alignType, aCancel, aHandled);
case EditSubAction::eInsertElement:
case EditSubAction::eInsertQuotedText: {
nsresult rv = MOZ_KnownLive(HTMLEditorRef()).WillInsert(aCancel);
@@ -813,6 +828,7 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
case EditSubAction::eUndo:
case EditSubAction::eRedo:
case EditSubAction::eRemoveList:
+ case EditSubAction::eSetOrClearAlignment:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
@@ -835,9 +851,6 @@ nsresult HTMLEditRules::DidDoAction(EditSubActionInfo& aInfo,
return NS_OK;
case EditSubAction::eDeleteSelectedContent:
return DidDeleteSelection();
- case EditSubAction::eSetOrClearAlignment:
- return MOZ_KnownLive(HTMLEditorRef())
- .MaybeInsertPaddingBRElementForEmptyLastLineAtSelection();
case EditSubAction::eSetPositionToAbsolute: {
nsresult rv =
MOZ_KnownLive(HTMLEditorRef())
@@ -860,6 +873,7 @@ nsresult HTMLEditRules::DidDoAction(EditSubActionInfo& aInfo,
case EditSubAction::eUndo:
case EditSubAction::eRedo:
case EditSubAction::eRemoveList:
+ case EditSubAction::eSetOrClearAlignment:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
@@ -6273,54 +6287,61 @@ bool HTMLEditor::IsEmptyBlockElement(Element& aElement,
return NS_SUCCEEDED(rv) && isEmpty;
}
-nsresult HTMLEditRules::WillAlign(const nsAString& aAlignType, bool* aCancel,
- bool* aHandled) {
- MOZ_ASSERT(IsEditorDataAvailable());
- MOZ_ASSERT(aCancel && aHandled);
+EditActionResult HTMLEditor::AlignAsSubAction(const nsAString& aAlignType) {
+ MOZ_ASSERT(IsEditActionDataAvailable());
- *aCancel = false;
- *aHandled = false;
+ AutoPlaceholderBatch treatAsOneTransaction(*this);
+ AutoEditSubActionNotifier startToHandleEditSubAction(
+ *this, EditSubAction::eSetOrClearAlignment, nsIEditor::eNext);
+
+ EditActionResult result = CanHandleHTMLEditSubAction();
+ if (NS_WARN_IF(result.Failed()) || result.Canceled()) {
+ return result;
+ }
// FYI: Ignore cancel result of WillInsert().
- nsresult rv = MOZ_KnownLive(HTMLEditorRef()).WillInsert();
+ nsresult rv = WillInsert();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
- return NS_ERROR_EDITOR_DESTROYED;
+ return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed, but ignored");
if (!SelectionRefPtr()->IsCollapsed()) {
- nsresult rv = MOZ_KnownLive(HTMLEditorRef())
- .MaybeExtendSelectionToHardLineEdgesForBlockEditAction();
+ nsresult rv = MaybeExtendSelectionToHardLineEdgesForBlockEditAction();
if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ return EditActionResult(rv);
}
}
- *aHandled = true;
+ // AlignContentsAtSelection() creates AutoSelectionRestorer. Therefore,
+ // we need to check whether we've been destroyed or not even if it returns
+ // NS_OK.
rv = AlignContentsAtSelection(aAlignType);
- if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
- NS_WARN_IF(!CanHandleEditAction())) {
- return NS_ERROR_EDITOR_DESTROYED;
+ if (NS_WARN_IF(Destroyed())) {
+ return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ return EditActionHandled(rv);
}
- return NS_OK;
+
+ rv = MaybeInsertPaddingBRElementForEmptyLastLineAtSelection();
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "MaybeInsertPaddingBRElementForEmptyLastLineAtSelection() failed");
+ return EditActionHandled(rv);
}
-nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
- AutoSelectionRestorer restoreSelectionLater(HTMLEditorRef());
+nsresult HTMLEditor::AlignContentsAtSelection(const nsAString& aAlignType) {
+ AutoSelectionRestorer restoreSelectionLater(*this);
// Convert the selection ranges into "promoted" selection ranges: This
// basically just expands the range to include the immediate block parent,
// and then further expands to include any ancestors whose children are all
// in the range
AutoTArray, 64> nodeArray;
- nsresult rv =
- MOZ_KnownLive(HTMLEditorRef())
- .SplitInlinesAndCollectEditTargetNodesInExtendedSelectionRanges(
- nodeArray, EditSubAction::eSetOrClearAlignment,
- HTMLEditor::CollectNonEditableNodes::Yes);
+ nsresult rv = SplitInlinesAndCollectEditTargetNodesInExtendedSelectionRanges(
+ nodeArray, EditSubAction::eSetOrClearAlignment,
+ CollectNonEditableNodes::Yes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -6328,7 +6349,7 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
// If we don't have any nodes, or we have only a single br, then we are
// creating an empty alignment div. We have to do some different things for
// these.
- bool emptyDiv = nodeArray.IsEmpty();
+ bool createEmptyDivElement = nodeArray.IsEmpty();
if (nodeArray.Length() == 1) {
OwningNonNull node = nodeArray[0];
@@ -6336,27 +6357,28 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
// The node is a table element, an hr, a paragraph, a div or a section
// header; in HTML 4, it can directly carry the ALIGN attribute and we
// don't need to make a div! If we are in CSS mode, all the work is done
- // in AlignBlock
- rv = AlignBlock(MOZ_KnownLive(*node->AsElement()), aAlignType,
- ResetAlignOf::OnlyDescendants);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
+ // in SetBlockElementAlign().
+ nsresult rv =
+ SetBlockElementAlign(MOZ_KnownLive(*node->AsElement()), aAlignType,
+ EditTarget::OnlyDescendantsExceptTable);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetBlockElementAlign() failed");
+ return rv;
}
if (TextEditUtils::IsBreak(node)) {
- // The special case emptyDiv code (below) that consumes BRs can cause
- // tables to split if the start node of the selection is not in a table
- // cell or caption, for example parent is a
. Avoid this unnecessary
- // splitting if possible by leaving emptyDiv FALSE so that we fall
- // through to the normal case alignment code.
+ // The special case createEmptyDivElement code (below) that consumes
+ // ` ` elements can cause tables to split if the start node of the
+ // selection is not in a table cell or caption, for example parent is a
+ // `
`. Avoid this unnecessary splitting if possible by leaving
+ // createEmptyDivElement false so that we fall through to the normal case
+ // alignment code.
//
- // XXX: It seems a little error prone for the emptyDiv special case code
- // to assume that the start node of the selection is the parent of the
- // single node in the nodeArray, as the paragraph above points out. Do we
- // rely on the selection start node because of the fact that nodeArray
- // can be empty? We should probably revisit this issue. - kin
+ // XXX: It seems a little error prone for the createEmptyDivElement
+ // special case code to assume that the start node of the selection
+ // is the parent of the single node in the nodeArray, as the
+ // paragraph above points out. Do we rely on the selection start
+ // node because of the fact that nodeArray can be empty? We should
+ // probably revisit this issue. - kin
nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
@@ -6367,128 +6389,145 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
return NS_ERROR_FAILURE;
}
nsINode* parent = atStartOfSelection.Container();
- emptyDiv = !HTMLEditUtils::IsTableElement(parent) ||
- HTMLEditUtils::IsTableCellOrCaption(*parent);
+ createEmptyDivElement = !HTMLEditUtils::IsTableElement(parent) ||
+ HTMLEditUtils::IsTableCellOrCaption(*parent);
}
}
- if (emptyDiv) {
- nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
- if (NS_WARN_IF(!firstRange)) {
- return NS_ERROR_FAILURE;
- }
- EditorDOMPoint atStartOfSelection(firstRange->StartRef());
- if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
- return NS_ERROR_FAILURE;
+ if (createEmptyDivElement) {
+ EditActionResult result =
+ AlignContentsAtSelectionWithEmptyDivElement(aAlignType);
+ NS_WARNING_ASSERTION(
+ result.Succeeded(),
+ "AlignContentsAtSelectionWithEmptyDivElement() failed");
+ if (result.Handled()) {
+ restoreSelectionLater.Abort();
}
+ return rv;
+ }
- SplitNodeResult splitNodeResult =
- MOZ_KnownLive(HTMLEditorRef())
- .MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
- atStartOfSelection);
- if (NS_WARN_IF(splitNodeResult.Failed())) {
- return splitNodeResult.Rv();
- }
+ rv = AlignNodesAndDescendants(nodeArray, aAlignType);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AlignNodesAndDescendants() failed");
+ return rv;
+}
+
+EditActionResult HTMLEditor::AlignContentsAtSelectionWithEmptyDivElement(
+ const nsAString& aAlignType) {
+ MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
+
+ nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
+ if (NS_WARN_IF(!firstRange)) {
+ return EditActionResult(NS_ERROR_FAILURE);
+ }
+
+ EditorDOMPoint atStartOfSelection(firstRange->StartRef());
+ if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
+ return EditActionResult(NS_ERROR_FAILURE);
+ }
+
+ SplitNodeResult splitNodeResult = MaybeSplitAncestorsForInsertWithTransaction(
+ *nsGkAtoms::div, atStartOfSelection);
+ if (NS_WARN_IF(splitNodeResult.Failed())) {
+ return EditActionResult(splitNodeResult.Rv());
+ }
+
+ EditorDOMPoint pointToInsertDiv(splitNodeResult.SplitPoint());
- // Consume a trailing br, if any. This is to keep an alignment from
- // creating extra lines, if possible.
- nsCOMPtr brContent =
- HTMLEditorRef().GetNextEditableHTMLNodeInBlock(
- splitNodeResult.SplitPoint());
- EditorDOMPoint pointToInsertDiv(splitNodeResult.SplitPoint());
- if (brContent && TextEditUtils::IsBreak(brContent)) {
+ // Consume a trailing br, if any. This is to keep an alignment from
+ // creating extra lines, if possible.
+ if (nsCOMPtr maybeBRContent =
+ GetNextEditableHTMLNodeInBlock(splitNodeResult.SplitPoint())) {
+ if (TextEditUtils::IsBreak(maybeBRContent) && pointToInsertDiv.GetChild()) {
// Making use of html structure... if next node after where we are
// putting our div is not a block, then the br we found is in same block
// we are, so it's safe to consume it.
- nsCOMPtr sibling;
- if (pointToInsertDiv.GetChild()) {
- sibling =
- HTMLEditorRef().GetNextHTMLSibling(pointToInsertDiv.GetChild());
- }
- if (sibling && !HTMLEditor::NodeIsBlockStatic(*sibling)) {
- AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertDiv);
- rv = MOZ_KnownLive(HTMLEditorRef())
- .DeleteNodeWithTransaction(*brContent);
- if (NS_WARN_IF(!CanHandleEditAction())) {
- return NS_ERROR_EDITOR_DESTROYED;
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ if (nsIContent* nextEditableSibling =
+ GetNextHTMLSibling(pointToInsertDiv.GetChild())) {
+ if (!HTMLEditor::NodeIsBlockStatic(*nextEditableSibling)) {
+ AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertDiv);
+ nsresult rv = DeleteNodeWithTransaction(*maybeBRContent);
+ if (NS_WARN_IF(Destroyed())) {
+ return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return EditActionResult(rv);
+ }
}
}
}
- RefPtr div =
- MOZ_KnownLive(HTMLEditorRef())
- .CreateNodeWithTransaction(*nsGkAtoms::div, pointToInsertDiv);
- if (NS_WARN_IF(!CanHandleEditAction())) {
- return NS_ERROR_EDITOR_DESTROYED;
- }
- if (NS_WARN_IF(!div)) {
- return NS_ERROR_FAILURE;
- }
- // Remember our new block for postprocessing
- HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement = div;
- // Set up the alignment on the div, using HTML or CSS
- rv = AlignBlock(*div, aAlignType, ResetAlignOf::OnlyDescendants);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Put in a padding element for empty last line so that it won't get
- // deleted.
- CreateElementResult createPaddingBRResult =
- MOZ_KnownLive(HTMLEditorRef())
- .InsertPaddingBRElementForEmptyLastLineWithTransaction(
- EditorDOMPoint(div, 0));
- if (NS_WARN_IF(createPaddingBRResult.Failed())) {
- return createPaddingBRResult.Rv();
- }
- EditorRawDOMPoint atStartOfDiv(div, 0);
- // Don't restore the selection
- restoreSelectionLater.Abort();
- ErrorResult error;
- SelectionRefPtr()->Collapse(atStartOfDiv, error);
- if (NS_WARN_IF(!CanHandleEditAction())) {
- error.SuppressException();
- return NS_ERROR_EDITOR_DESTROYED;
- }
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- return NS_OK;
}
- // Next we detect all the transitions in the array, where a transition
- // means that adjacent nodes in the array don't have the same parent.
+ RefPtr divElement =
+ CreateNodeWithTransaction(*nsGkAtoms::div, pointToInsertDiv);
+ if (NS_WARN_IF(Destroyed())) {
+ return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
+ }
+ if (NS_WARN_IF(!divElement)) {
+ return EditActionResult(NS_ERROR_FAILURE);
+ }
+ // Remember our new block for postprocessing
+ TopLevelEditSubActionDataRef().mNewBlockElement = divElement;
+ // Set up the alignment on the div, using HTML or CSS
+ nsresult rv = SetBlockElementAlign(*divElement, aAlignType,
+ EditTarget::OnlyDescendantsExceptTable);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return EditActionResult(rv);
+ }
+ // Put in a padding element for empty last line so that it won't get
+ // deleted.
+ CreateElementResult createPaddingBRResult =
+ InsertPaddingBRElementForEmptyLastLineWithTransaction(
+ EditorDOMPoint(divElement, 0));
+ if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+ return EditActionResult(createPaddingBRResult.Rv());
+ }
+ EditorRawDOMPoint atStartOfDiv(divElement, 0);
+ ErrorResult error;
+ SelectionRefPtr()->Collapse(atStartOfDiv, error);
+ if (NS_WARN_IF(Destroyed())) {
+ error.SuppressException();
+ return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
+ }
+ NS_WARNING_ASSERTION(!error.Failed(), "Selection::Collapse() failed");
+ return EditActionHandled(error.StealNSResult());
+}
+nsresult HTMLEditor::AlignNodesAndDescendants(
+ nsTArray>& aArrayOfNodes,
+ const nsAString& aAlignType) {
+ // Detect all the transitions in the array, where a transition means that
+ // adjacent nodes in the array don't have the same parent.
AutoTArray transitionList;
- HTMLEditor::MakeTransitionList(nodeArray, transitionList);
+ HTMLEditor::MakeTransitionList(aArrayOfNodes, transitionList);
// Okay, now go through all the nodes and give them an align attrib or put
// them in a div, or whatever is appropriate. Woohoo!
- nsCOMPtr curDiv;
- bool useCSS = HTMLEditorRef().IsCSSEnabled();
+ RefPtr createdDivElement;
+ bool useCSS = IsCSSEnabled();
int32_t indexOfTransitionList = -1;
- for (OwningNonNull& curNode : nodeArray) {
+ for (OwningNonNull& curNode : aArrayOfNodes) {
++indexOfTransitionList;
// Ignore all non-editable nodes. Leave them be.
- if (!HTMLEditorRef().IsEditable(curNode)) {
+ if (!IsEditable(curNode)) {
continue;
}
// The node is a table element, an hr, a paragraph, a div or a section
// header; in HTML 4, it can directly carry the ALIGN attribute and we
// don't need to nest it, just set the alignment. In CSS, assign the
- // corresponding CSS styles in AlignBlock
+ // corresponding CSS styles in SetBlockElementAlign().
if (HTMLEditUtils::SupportsAlignAttr(*curNode)) {
- rv = AlignBlock(MOZ_KnownLive(*curNode->AsElement()), aAlignType,
- ResetAlignOf::ElementAndDescendants);
+ nsresult rv =
+ SetBlockElementAlign(MOZ_KnownLive(*curNode->AsElement()), aAlignType,
+ EditTarget::NodeAndDescendantsExceptTable);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- // Clear out curDiv so that we don't put nodes after this one into it
- curDiv = nullptr;
+ // Clear out createdDivElement so that we don't put nodes after this one
+ // into it
+ createdDivElement = nullptr;
continue;
}
@@ -6500,12 +6539,11 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
// Skip insignificant formatting text nodes to prevent unnecessary
// structure splitting!
bool isEmptyTextNode = false;
- if (curNode->GetAsText() &&
+ if (curNode->IsText() &&
((HTMLEditUtils::IsTableElement(atCurNode.GetContainer()) &&
!HTMLEditUtils::IsTableCellOrCaption(*atCurNode.GetContainer())) ||
HTMLEditUtils::IsList(atCurNode.GetContainer()) ||
- (NS_SUCCEEDED(
- HTMLEditorRef().IsEmptyNode(curNode, &isEmptyTextNode)) &&
+ (NS_SUCCEEDED(IsEmptyNode(curNode, &isEmptyTextNode)) &&
isEmptyTextNode))) {
continue;
}
@@ -6515,82 +6553,84 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
if (HTMLEditUtils::IsListItem(curNode) || HTMLEditUtils::IsList(curNode)) {
Element* listOrListItemElement = curNode->AsElement();
AutoEditorDOMPointOffsetInvalidator lockChild(atCurNode);
- rv = MOZ_KnownLive(HTMLEditorRef())
- .RemoveAlignFromDescendants(
- MOZ_KnownLive(*listOrListItemElement), aAlignType,
- HTMLEditor::EditTarget::OnlyDescendantsExceptTable);
+ nsresult rv = RemoveAlignFromDescendants(
+ MOZ_KnownLive(*listOrListItemElement), aAlignType,
+ EditTarget::OnlyDescendantsExceptTable);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
+
if (useCSS) {
- HTMLEditorRef().mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
+ mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
MOZ_KnownLive(listOrListItemElement), nullptr, nsGkAtoms::align,
&aAlignType, false);
- if (NS_WARN_IF(!CanHandleEditAction())) {
+ if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
- curDiv = nullptr;
+ createdDivElement = nullptr;
continue;
}
+
if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
// If we don't use CSS, add a content to list element: they have to
// be inside another list, i.e., >= second level of nesting.
// XXX AlignContentsInAllTableCellsAndListItems() handles only list
// item elements and table cells. Is it intentional? Why don't
// we need to align contents in other type blocks?
- rv = MOZ_KnownLive(HTMLEditorRef())
- .AlignContentsInAllTableCellsAndListItems(
- MOZ_KnownLive(*listOrListItemElement), aAlignType);
+ nsresult rv = AlignContentsInAllTableCellsAndListItems(
+ MOZ_KnownLive(*listOrListItemElement), aAlignType);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- curDiv = nullptr;
+ createdDivElement = nullptr;
continue;
}
- // Clear out curDiv so that we don't put nodes after this one into it
+
+ // Clear out createdDivElement so that we don't put nodes after this one
+ // into it
}
// Need to make a div to put things in if we haven't already, or if this
// node doesn't go in div we used earlier.
- if (!curDiv || transitionList[indexOfTransitionList]) {
+ if (!createdDivElement || transitionList[indexOfTransitionList]) {
// First, check that our element can contain a div.
- if (!HTMLEditorRef().CanContainTag(*atCurNode.GetContainer(),
- *nsGkAtoms::div)) {
- // Cancelled
+ if (!CanContainTag(*atCurNode.GetContainer(), *nsGkAtoms::div)) {
+ // XXX Why do we return NS_OK here rather than returning error or
+ // doing continue?
return NS_OK;
}
SplitNodeResult splitNodeResult =
- MOZ_KnownLive(HTMLEditorRef())
- .MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
- atCurNode);
+ MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
+ atCurNode);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
- curDiv = MOZ_KnownLive(HTMLEditorRef())
- .CreateNodeWithTransaction(*nsGkAtoms::div,
- splitNodeResult.SplitPoint());
- if (NS_WARN_IF(!CanHandleEditAction())) {
+ createdDivElement = CreateNodeWithTransaction(
+ *nsGkAtoms::div, splitNodeResult.SplitPoint());
+ if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
- if (NS_WARN_IF(!curDiv)) {
+ if (NS_WARN_IF(!createdDivElement)) {
return NS_ERROR_FAILURE;
}
// Remember our new block for postprocessing
- HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement = curDiv;
+ TopLevelEditSubActionDataRef().mNewBlockElement = createdDivElement;
// Set up the alignment on the div
- rv = AlignBlock(*curDiv, aAlignType, ResetAlignOf::OnlyDescendants);
+ nsresult rv =
+ SetBlockElementAlign(*createdDivElement, aAlignType,
+ EditTarget::OnlyDescendantsExceptTable);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to align the
");
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "SetBlockElementAlign() failed, but ignored");
}
// Tuck the node into the end of the active div
- rv = MOZ_KnownLive(HTMLEditorRef())
- .MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
- *curDiv);
- if (NS_WARN_IF(!CanHandleEditAction())) {
+ nsresult rv = MoveNodeToEndWithTransaction(
+ MOZ_KnownLive(*curNode->AsContent()), *createdDivElement);
+ if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -9681,70 +9721,62 @@ void HTMLEditRules::CheckInterlinePosition() {
}
}
-nsresult HTMLEditRules::AdjustSelection(nsIEditor::EDirection aAction) {
- MOZ_ASSERT(IsEditorDataAvailable());
-
- // if the selection isn't collapsed, do nothing.
- // moose: one thing to do instead is check for the case of
- // only a single break selected, and collapse it. Good thing? Beats me.
- if (!SelectionRefPtr()->IsCollapsed()) {
- return NS_OK;
- }
+nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
+ nsIEditor::EDirection aDirectionAndAmount) {
+ MOZ_ASSERT(IsEditActionDataAvailable());
+ MOZ_ASSERT(SelectionRefPtr()->IsCollapsed());
- // get the (collapsed) selection location
EditorDOMPoint point(EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!point.IsSet())) {
return NS_ERROR_FAILURE;
}
- // are we in an editable node?
- while (!HTMLEditorRef().IsEditable(point.GetContainer())) {
- // scan up the tree until we find an editable place to be
+ // If selection start is not editable, climb up the tree until editable one.
+ while (!IsEditable(point.GetContainer())) {
point.Set(point.GetContainer());
if (NS_WARN_IF(!point.IsSet())) {
return NS_ERROR_FAILURE;
}
}
- // make sure we aren't in an empty block - user will see no cursor. If this
- // is happening, put a in the block if allowed.
- RefPtr theblock = HTMLEditorRef().GetBlock(*point.GetContainer());
-
- if (theblock && HTMLEditorRef().IsEditable(theblock)) {
- bool isEmptyNode;
- nsresult rv =
- HTMLEditorRef().IsEmptyNode(theblock, &isEmptyNode, false, false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // check if br can go into the destination node
- if (isEmptyNode &&
- HTMLEditorRef().CanContainTag(*point.GetContainer(), *nsGkAtoms::br)) {
- Element* rootElement = HTMLEditorRef().GetRoot();
- if (NS_WARN_IF(!rootElement)) {
- return NS_ERROR_FAILURE;
+ // If caret is in empty block element, we need to insert a ` ` element
+ // because the block should have one-line height.
+ if (RefPtr blockElement = GetBlock(*point.GetContainer())) {
+ if (IsEditable(blockElement)) {
+ bool isEmptyNode;
+ nsresult rv = IsEmptyNode(blockElement, &isEmptyNode, false, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
}
- if (point.GetContainer() == rootElement) {
- // Our root node is completely empty. Don't add a here.
- // AfterEditInner() will add one for us when it calls
- // TextEditor::MaybeCreatePaddingBRElementForEmptyEditor()!
+ if (isEmptyNode && CanContainTag(*point.GetContainer(), *nsGkAtoms::br)) {
+ Element* bodyOrDocumentElement = GetRoot();
+ if (NS_WARN_IF(!bodyOrDocumentElement)) {
+ return NS_ERROR_FAILURE;
+ }
+ if (point.GetContainer() == bodyOrDocumentElement) {
+ // Our root node is completely empty. Don't add a here.
+ // AfterEditInner() will add one for us when it calls
+ // TextEditor::MaybeCreatePaddingBRElementForEmptyEditor().
+ // XXX This kind of dependency between methods makes us spaghetti.
+ // Let's handle it here later.
+ // XXX This looks odd check. If active editing host is not a
+ // ``, what are we doing?
+ return NS_OK;
+ }
+ CreateElementResult createPaddingBRResult =
+ InsertPaddingBRElementForEmptyLastLineWithTransaction(point);
+ if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+ return createPaddingBRResult.Rv();
+ }
return NS_OK;
}
-
- // we know we can skip the rest of this routine given the cirumstance
- CreateElementResult createPaddingBRResult =
- MOZ_KnownLive(HTMLEditorRef())
- .InsertPaddingBRElementForEmptyLastLineWithTransaction(point);
- if (NS_WARN_IF(createPaddingBRResult.Failed())) {
- return createPaddingBRResult.Rv();
- }
- return NS_OK;
}
}
- // are we in a text node?
+ // XXX Perhaps, we should do something if we're in a data node but not
+ // a text node.
if (point.IsInTextNode()) {
- return NS_OK; // we LIKE it when we are in a text node. that RULZ
+ return NS_OK;
}
// Do we need to insert a padding element for empty last line? We do
@@ -9753,118 +9785,125 @@ nsresult HTMLEditRules::AdjustSelection(nsIEditor::EDirection aAction) {
// 2) prior node is a br AND
// 3) that br is not visible
- nsCOMPtr nearNode =
- HTMLEditorRef().GetPreviousEditableHTMLNode(point);
- if (nearNode) {
- // is nearNode also a descendant of same block?
- RefPtr block = HTMLEditorRef().GetBlock(*point.GetContainer());
- RefPtr nearBlock = HTMLEditorRef().GetBlockNodeParent(nearNode);
- if (block && block == nearBlock) {
- if (nearNode && TextEditUtils::IsBreak(nearNode)) {
- if (!HTMLEditorRef().IsVisibleBRElement(nearNode)) {
- // need to insert special moz BR. Why? Because if we don't
- // the user will see no new line for the break. Also, things
- // like table cells won't grow in height.
- CreateElementResult createPaddingBRResult =
- MOZ_KnownLive(HTMLEditorRef())
- .InsertPaddingBRElementForEmptyLastLineWithTransaction(point);
- if (NS_WARN_IF(createPaddingBRResult.Failed())) {
- return createPaddingBRResult.Rv();
- }
- point.Set(createPaddingBRResult.GetNewNode());
- // Selection stays *before* padding element for empty last
- // line, sticking to it.
- ErrorResult error;
- SelectionRefPtr()->SetInterlinePosition(true, error);
- if (NS_WARN_IF(!CanHandleEditAction())) {
- error.SuppressException();
- return NS_ERROR_EDITOR_DESTROYED;
- }
- NS_WARNING_ASSERTION(!error.Failed(),
+ if (nsCOMPtr previousEditableContent =
+ GetPreviousEditableHTMLNode(point)) {
+ RefPtr blockElementAtCaret = GetBlock(*point.GetContainer());
+ RefPtr blockElementParentAtPreviousEditableContent =
+ GetBlockNodeParent(previousEditableContent);
+ // If previous editable content of caret is in same block and a ` `
+ // element, we need to adjust interline position.
+ if (blockElementAtCaret &&
+ blockElementAtCaret == blockElementParentAtPreviousEditableContent &&
+ previousEditableContent &&
+ TextEditUtils::IsBreak(previousEditableContent)) {
+ // If it's an invisible ` ` element, we need to insert a padding
+ // ` ` element for making empty line have one-line height.
+ if (!IsVisibleBRElement(previousEditableContent)) {
+ CreateElementResult createPaddingBRResult =
+ InsertPaddingBRElementForEmptyLastLineWithTransaction(point);
+ if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+ return createPaddingBRResult.Rv();
+ }
+ point.Set(createPaddingBRResult.GetNewNode());
+ // Selection stays *before* padding ` ` element for empty last
+ // line, sticking to it.
+ IgnoredErrorResult ignoredError;
+ SelectionRefPtr()->SetInterlinePosition(true, ignoredError);
+ if (NS_WARN_IF(Destroyed())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ NS_WARNING_ASSERTION(!ignoredError.Failed(),
+ "Failed to set interline position");
+ ErrorResult error;
+ SelectionRefPtr()->Collapse(point, error);
+ if (NS_WARN_IF(Destroyed())) {
+ error.SuppressException();
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+ }
+ // If it's a visible ` ` element and next editable content is a
+ // padding ` ` element, we need to set interline position.
+ else if (nsIContent* nextEditableContentInBlock =
+ GetNextEditableHTMLNodeInBlock(*previousEditableContent)) {
+ if (EditorBase::IsPaddingBRElementForEmptyLastLine(
+ *nextEditableContentInBlock)) {
+ // Make it stick to the padding ` ` element so that it will be
+ // on blank line.
+ IgnoredErrorResult ignoredError;
+ SelectionRefPtr()->SetInterlinePosition(true, ignoredError);
+ NS_WARNING_ASSERTION(!ignoredError.Failed(),
"Failed to set interline position");
- error = NS_OK;
- SelectionRefPtr()->Collapse(point, error);
- if (NS_WARN_IF(!CanHandleEditAction())) {
- error.SuppressException();
- return NS_ERROR_EDITOR_DESTROYED;
- }
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- } else {
- nsCOMPtr nextNode =
- HTMLEditorRef().GetNextEditableHTMLNodeInBlock(*nearNode);
- if (nextNode &&
- EditorBase::IsPaddingBRElementForEmptyLastLine(*nextNode)) {
- // Selection between a element and a padding element for
- // empty last line. Make it stick to the padding element so
- // that it will be on blank line.
- IgnoredErrorResult ignoredError;
- SelectionRefPtr()->SetInterlinePosition(true, ignoredError);
- NS_WARNING_ASSERTION(!ignoredError.Failed(),
- "Failed to set interline position");
- }
}
}
}
}
- // we aren't in a textnode: are we adjacent to text or a break or an image?
- nearNode = HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(point);
- if (nearNode &&
- (TextEditUtils::IsBreak(nearNode) || EditorBase::IsTextNode(nearNode) ||
- HTMLEditUtils::IsImage(nearNode) ||
- nearNode->IsHTMLElement(nsGkAtoms::hr))) {
- // this is a good place for the caret to be
- return NS_OK;
+ // If previous editable content in same block is ` `, text node, ``
+ // or ``, current caret position is fine.
+ if (nsIContent* previousEditableContentInBlock =
+ GetPreviousEditableHTMLNodeInBlock(point)) {
+ if (TextEditUtils::IsBreak(previousEditableContentInBlock) ||
+ EditorBase::IsTextNode(previousEditableContentInBlock) ||
+ HTMLEditUtils::IsImage(previousEditableContentInBlock) ||
+ previousEditableContentInBlock->IsHTMLElement(nsGkAtoms::hr)) {
+ return NS_OK;
+ }
}
- nearNode = HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
- if (nearNode &&
- (TextEditUtils::IsBreak(nearNode) || EditorBase::IsTextNode(nearNode) ||
- nearNode->IsAnyOfHTMLElements(nsGkAtoms::img, nsGkAtoms::hr))) {
- return NS_OK; // this is a good place for the caret to be
+ // If next editable content in same block is ` `, text node, `` or
+ // ``, current caret position is fine.
+ if (nsIContent* nextEditableContentInBlock =
+ GetNextEditableHTMLNodeInBlock(point)) {
+ if (TextEditUtils::IsBreak(nextEditableContentInBlock) ||
+ EditorBase::IsTextNode(nextEditableContentInBlock) ||
+ nextEditableContentInBlock->IsAnyOfHTMLElements(nsGkAtoms::img,
+ nsGkAtoms::hr)) {
+ return NS_OK;
+ }
}
- // look for a nearby text node.
- // prefer the correct direction.
- nearNode = FindNearEditableNode(point, aAction);
- if (!nearNode) {
+ // Otherwise, look for a near editable content towards edit action direction.
+
+ // If there is no editable content, keep current caret position.
+ nsIContent* nearEditableContent =
+ FindNearEditableContent(point, aDirectionAndAmount);
+ if (!nearEditableContent) {
return NS_OK;
}
- EditorDOMPoint pt = HTMLEditorRef().GetGoodCaretPointFor(*nearNode, aAction);
+ EditorDOMPoint pointToPutCaret =
+ GetGoodCaretPointFor(*nearEditableContent, aDirectionAndAmount);
+ if (NS_WARN_IF(!pointToPutCaret.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
ErrorResult error;
- SelectionRefPtr()->Collapse(pt, error);
- if (NS_WARN_IF(!CanHandleEditAction())) {
+ SelectionRefPtr()->Collapse(pointToPutCaret, error);
+ if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- return NS_OK;
+ NS_WARNING_ASSERTION(!error.Failed(), "Selection::Collapse() failed");
+ return error.StealNSResult();
}
template
-nsIContent* HTMLEditRules::FindNearEditableNode(
+nsIContent* HTMLEditor::FindNearEditableContent(
const EditorDOMPointBase& aPoint,
nsIEditor::EDirection aDirection) {
- MOZ_ASSERT(IsEditorDataAvailable());
-
- if (NS_WARN_IF(!aPoint.IsSet())) {
- return nullptr;
- }
+ MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aPoint.IsSetAndValid());
- nsIContent* nearNode = nullptr;
+ nsIContent* editableContent = nullptr;
if (aDirection == nsIEditor::ePrevious) {
- nearNode = HTMLEditorRef().GetPreviousEditableHTMLNode(aPoint);
- if (!nearNode) {
+ editableContent = GetPreviousEditableHTMLNode(aPoint);
+ if (!editableContent) {
return nullptr; // Not illegal.
}
} else {
- nearNode = HTMLEditorRef().GetNextEditableHTMLNode(aPoint);
- if (NS_WARN_IF(!nearNode)) {
+ editableContent = GetNextEditableHTMLNode(aPoint);
+ if (NS_WARN_IF(!editableContent)) {
// Perhaps, illegal because the node pointed by aPoint isn't editable
// and nobody of previous nodes is editable.
return nullptr;
@@ -9873,32 +9912,32 @@ nsIContent* HTMLEditRules::FindNearEditableNode(
// scan in the right direction until we find an eligible text node,
// but don't cross any breaks, images, or table elements.
- // XXX This comment sounds odd. |nearNode| may have already crossed breaks
- // and/or images.
- while (nearNode && !(EditorBase::IsTextNode(nearNode) ||
- TextEditUtils::IsBreak(nearNode) ||
- HTMLEditUtils::IsImage(nearNode))) {
+ // XXX This comment sounds odd. editableContent may have already crossed
+ // breaks and/or images if they are non-editable.
+ while (editableContent && !EditorBase::IsTextNode(editableContent) &&
+ !TextEditUtils::IsBreak(editableContent) &&
+ !HTMLEditUtils::IsImage(editableContent)) {
if (aDirection == nsIEditor::ePrevious) {
- nearNode = HTMLEditorRef().GetPreviousEditableHTMLNode(*nearNode);
- if (NS_WARN_IF(!nearNode)) {
+ editableContent = GetPreviousEditableHTMLNode(*editableContent);
+ if (NS_WARN_IF(!editableContent)) {
return nullptr;
}
} else {
- nearNode = HTMLEditorRef().GetNextEditableHTMLNode(*nearNode);
- if (NS_WARN_IF(!nearNode)) {
+ editableContent = GetNextEditableHTMLNode(*editableContent);
+ if (NS_WARN_IF(!editableContent)) {
return nullptr;
}
}
}
// don't cross any table elements
- if (HTMLEditor::NodesInDifferentTableElements(*nearNode,
+ if (HTMLEditor::NodesInDifferentTableElements(*editableContent,
*aPoint.GetContainer())) {
return nullptr;
}
// otherwise, ok, we have found a good spot to put the selection
- return nearNode;
+ return editableContent;
}
// static
@@ -9994,14 +10033,8 @@ nsresult HTMLEditRules::RemoveEmptyNodesInChangedRange() {
// These node types are candidates if selection is not in them. If
// it is one of these, don't delete if selection inside. This is so
// we can create empty headings, etc., for the user to type into.
- bool isSelectionEndInNode;
- rv = SelectionEndpointInNode(node, &isSelectionEndInNode);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isSelectionEndInNode) {
- isCandidate = true;
- }
+ isCandidate = !HTMLEditorRef().StartOrEndOfSelectionRangesIsIn(
+ *node->AsContent());
}
}
@@ -10075,25 +10108,18 @@ nsresult HTMLEditRules::RemoveEmptyNodesInChangedRange() {
return NS_OK;
}
-nsresult HTMLEditRules::SelectionEndpointInNode(nsINode* aNode, bool* aResult) {
- MOZ_ASSERT(IsEditorDataAvailable());
-
- NS_ENSURE_TRUE(aNode && aResult, NS_ERROR_NULL_POINTER);
-
- *aResult = false;
+bool HTMLEditor::StartOrEndOfSelectionRangesIsIn(nsIContent& aContent) const {
+ MOZ_ASSERT(IsEditActionDataAvailable());
- uint32_t rangeCount = SelectionRefPtr()->RangeCount();
- for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
- RefPtr range = SelectionRefPtr()->GetRangeAt(rangeIdx);
+ for (uint32_t i = 0; i < SelectionRefPtr()->RangeCount(); ++i) {
+ nsRange* range = SelectionRefPtr()->GetRangeAt(i);
nsINode* startContainer = range->GetStartContainer();
if (startContainer) {
- if (aNode == startContainer) {
- *aResult = true;
- return NS_OK;
+ if (&aContent == startContainer) {
+ return true;
}
- if (EditorUtils::IsDescendantOf(*startContainer, *aNode)) {
- *aResult = true;
- return NS_OK;
+ if (EditorUtils::IsDescendantOf(*startContainer, aContent)) {
+ return true;
}
}
nsINode* endContainer = range->GetEndContainer();
@@ -10101,17 +10127,15 @@ nsresult HTMLEditRules::SelectionEndpointInNode(nsINode* aNode, bool* aResult) {
continue;
}
if (endContainer) {
- if (aNode == endContainer) {
- *aResult = true;
- return NS_OK;
+ if (&aContent == endContainer) {
+ return true;
}
- if (EditorUtils::IsDescendantOf(*endContainer, *aNode)) {
- *aResult = true;
- return NS_OK;
+ if (EditorUtils::IsDescendantOf(*endContainer, aContent)) {
+ return true;
}
}
}
- return NS_OK;
+ return false;
}
nsresult HTMLEditor::LiftUpListItemElement(
@@ -10275,12 +10299,12 @@ nsresult HTMLEditor::DestroyListStructureRecursively(Element& aListElement) {
return NS_OK;
}
-nsresult HTMLEditRules::ConfirmSelectionInBody() {
- MOZ_ASSERT(IsEditorDataAvailable());
+nsresult HTMLEditor::EnsureSelectionInBodyOrDocumentElement() {
+ MOZ_ASSERT(IsEditActionDataAvailable());
- Element* rootElement = HTMLEditorRef().GetRoot();
- if (NS_WARN_IF(!rootElement)) {
- return NS_ERROR_UNEXPECTED;
+ RefPtr bodyOrDocumentElement = GetRoot();
+ if (NS_WARN_IF(!bodyOrDocumentElement)) {
+ return NS_ERROR_FAILURE;
}
EditorRawDOMPoint selectionStartPoint(
@@ -10289,6 +10313,12 @@ nsresult HTMLEditRules::ConfirmSelectionInBody() {
return NS_ERROR_FAILURE;
}
+ // XXX This does wrong things. Web apps can put any elements as sibling
+ // of `` element. Therefore, this collapses `Selection` into
+ // the `` element which `HTMLDocument.body` is set to. So,
+ // this makes users impossible to modify content outside of the
+ // `` element even if caret is in an editing host.
+
// Check that selection start container is inside the element.
// XXXsmaug this code is insane.
nsINode* temp = selectionStartPoint.GetContainer();
@@ -10299,13 +10329,14 @@ nsresult HTMLEditRules::ConfirmSelectionInBody() {
// If we aren't in the element, force the issue.
if (!temp) {
IgnoredErrorResult ignoredError;
- SelectionRefPtr()->Collapse(RawRangeBoundary(rootElement, 0), ignoredError);
- if (NS_WARN_IF(!CanHandleEditAction())) {
+ SelectionRefPtr()->Collapse(RawRangeBoundary(bodyOrDocumentElement, 0),
+ ignoredError);
+ if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
- "Failed to collapse selection at start of the root element");
+ "Selection::Collapse() with start of editing host failed, but ignored");
return NS_OK;
}
@@ -10325,13 +10356,14 @@ nsresult HTMLEditRules::ConfirmSelectionInBody() {
// If we aren't in the element, force the issue.
if (!temp) {
IgnoredErrorResult ignoredError;
- SelectionRefPtr()->Collapse(RawRangeBoundary(rootElement, 0), ignoredError);
- if (NS_WARN_IF(!CanHandleEditAction())) {
+ SelectionRefPtr()->Collapse(RawRangeBoundary(bodyOrDocumentElement, 0),
+ ignoredError);
+ if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
- "Failed to collapse selection at start of the root element");
+ "Selection::Collapse() with start of editing host failed, but ignored");
}
return NS_OK;
@@ -10772,61 +10804,31 @@ nsresult HTMLEditor::EnsureHardLineEndsWithLastChildOf(
return NS_OK;
}
-nsresult HTMLEditRules::AlignBlock(Element& aElement,
- const nsAString& aAlignType,
- ResetAlignOf aResetAlignOf) {
- MOZ_ASSERT(IsEditorDataAvailable());
-
- if (!HTMLEditor::NodeIsBlockStatic(aElement) &&
- !aElement.IsHTMLElement(nsGkAtoms::hr)) {
- // We deal only with blocks; early way out
- return NS_OK;
- }
+nsresult HTMLEditor::SetBlockElementAlign(Element& aBlockOrHRElement,
+ const nsAString& aAlignType,
+ EditTarget aEditTarget) {
+ MOZ_ASSERT(IsEditActionDataAvailable());
+ MOZ_ASSERT(HTMLEditor::NodeIsBlockStatic(aBlockOrHRElement) ||
+ aBlockOrHRElement.IsHTMLElement(nsGkAtoms::hr));
+ MOZ_ASSERT(IsCSSEnabled() ||
+ HTMLEditUtils::SupportsAlignAttr(aBlockOrHRElement));
- if (!aElement.IsHTMLElement(nsGkAtoms::table)) {
+ if (!aBlockOrHRElement.IsHTMLElement(nsGkAtoms::table)) {
nsresult rv =
- MOZ_KnownLive(HTMLEditorRef())
- .RemoveAlignFromDescendants(
- aElement, aAlignType,
- aResetAlignOf == ResetAlignOf::OnlyDescendants
- ? HTMLEditor::EditTarget::OnlyDescendantsExceptTable
- : HTMLEditor::EditTarget::NodeAndDescendantsExceptTable);
+ RemoveAlignFromDescendants(aBlockOrHRElement, aAlignType, aEditTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
- if (HTMLEditorRef().IsCSSEnabled()) {
- // Let's use CSS alignment; we use margin-left and margin-right for tables
- // and text-align for other block-level elements
- nsresult rv = MOZ_KnownLive(HTMLEditorRef())
- .SetAttributeOrEquivalent(&aElement, nsGkAtoms::align,
- aAlignType, false);
- if (NS_WARN_IF(!CanHandleEditAction())) {
- return NS_ERROR_EDITOR_DESTROYED;
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
-
- // HTML case; this code is supposed to be called ONLY if the element
- // supports the align attribute but we'll never know...
- if (NS_WARN_IF(!HTMLEditUtils::SupportsAlignAttr(aElement))) {
- // XXX error?
- return NS_OK;
- }
-
- nsresult rv = MOZ_KnownLive(HTMLEditorRef())
- .SetAttributeOrEquivalent(&aElement, nsGkAtoms::align,
- aAlignType, false);
- if (NS_WARN_IF(!CanHandleEditAction())) {
+ nsresult rv = SetAttributeOrEquivalent(&aBlockOrHRElement, nsGkAtoms::align,
+ aAlignType, false);
+ if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "SetAttributeOrEquivalent() failed to set `align` attribute or property");
+ return rv;
}
nsresult HTMLEditor::ChangeMarginStart(Element& aElement,
diff --git a/editor/libeditor/HTMLEditRules.h b/editor/libeditor/HTMLEditRules.h
index 6809c5e7eceb1..893b5da7779ac 100644
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -113,19 +113,6 @@ class HTMLEditRules : public TextEditRules {
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult DidDeleteSelection();
- /**
- * Called before aligning contents around Selection. This method actually
- * sets align attributes to align contents.
- *
- * @param aAlignType New align attribute value where the contents
- * should be aligned to.
- * @param aCancel Returns true if the operation is canceled.
- * @param aHandled Returns true if the edit action is handled.
- */
- MOZ_CAN_RUN_SCRIPT
- nsresult WillAlign(const nsAString& aAlignType, bool* aCancel,
- bool* aHandled);
-
/**
* Called before changing absolute positioned element to static positioned.
* This method actually changes the position property of nearest absolute
@@ -191,20 +178,6 @@ class HTMLEditRules : public TextEditRules {
MOZ_CAN_RUN_SCRIPT
MOZ_MUST_USE nsresult DidAbsolutePosition();
- /**
- * AlignContentsAtSelection() aligns contents around Selection to aAlignType.
- * This creates AutoSelectionRestorer. Therefore, even if this returns
- * NS_OK, CanHandleEditAction() may return false if the editor is destroyed
- * during restoring the Selection. So, every caller needs to check if
- * CanHandleEditAction() returns true before modifying the DOM tree or
- * changing Selection.
- *
- * @param aAlignType New align attribute value where the contents
- * should be aligned to.
- */
- MOZ_CAN_RUN_SCRIPT
- MOZ_MUST_USE nsresult AlignContentsAtSelection(const nsAString& aAlignType);
-
nsresult AppendInnerFormatNodes(nsTArray>& aArray,
nsINode* aNode);
nsresult GetFormatString(nsINode* aNode, nsAString& outFormat);
@@ -236,33 +209,6 @@ class HTMLEditRules : public TextEditRules {
void CheckInterlinePosition();
- /**
- * AdjustSelection() may adjust Selection range to nearest editable content.
- * Despite of the name, this may change the DOM tree. If it needs to create
- * a to put caret, this tries to create a element.
- *
- * @param aAction Maybe used to look for a good point to put caret.
- */
- MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
- AdjustSelection(nsIEditor::EDirection aAction);
-
- /**
- * FindNearEditableNode() tries to find an editable node near aPoint.
- *
- * @param aPoint The DOM point where to start to search from.
- * @param aDirection If nsIEditor::ePrevious is set, this searches an
- * editable node from next nodes. Otherwise, from
- * previous nodes.
- * @return If found, returns non-nullptr. Otherwise, nullptr.
- * Note that if found node is in different table element,
- * this returns nullptr.
- * And also if aDirection is not nsIEditor::ePrevious,
- * the result may be the node pointed by aPoint.
- */
- template
- nsIContent* FindNearEditableNode(const EditorDOMPointBase& aPoint,
- nsIEditor::EDirection aDirection);
-
/**
* RemoveEmptyNodesInChangedRange() removes all empty nodes in
* TopLevelEditSubActionData::mChangedRange. However, if mail-cite node has
@@ -276,35 +222,6 @@ class HTMLEditRules : public TextEditRules {
MOZ_CAN_RUN_SCRIPT
MOZ_MUST_USE nsresult RemoveEmptyNodesInChangedRange();
- nsresult SelectionEndpointInNode(nsINode* aNode, bool* aResult);
-
- /**
- * ConfirmSelectionInBody() makes sure that Selection is in editor root
- * element typically element (see HTMLEditor::UpdateRootElement())
- * and only one Selection range.
- * XXX This method is not necessary because even if selection is outside the
- * element, elements outside the element should be
- * editable, e.g., any element can be inserted siblings as element
- * and other browsers allow to edit such elements.
- */
- MOZ_MUST_USE nsresult ConfirmSelectionInBody();
-
- /**
- * AlignBlock() resets align attribute, text-align property, etc first.
- * Then, aligns contents of aElement on aAlignType.
- *
- * @param aElement The element whose contents will be aligned.
- * @param aAlignType Boundary or "center" which contents should be
- * aligned on.
- * @param aResetAlignOf Resets align of whether element and its
- * descendants or only descendants.
- */
- enum class ResetAlignOf { ElementAndDescendants, OnlyDescendants };
- MOZ_CAN_RUN_SCRIPT
- MOZ_MUST_USE nsresult AlignBlock(Element& aElement,
- const nsAString& aAlignType,
- ResetAlignOf aResetAlignOf);
-
/**
* DocumentModifiedWorker() is called by DocumentModified() either
* synchronously or asynchronously.
diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp
index 6c088ad92f71c..318855ff8264d 100644
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -2205,28 +2205,9 @@ nsresult HTMLEditor::AlignAsAction(const nsAString& aAlignType,
return NS_ERROR_NOT_INITIALIZED;
}
- // Protect the edit rules object from dying
- RefPtr rules(mRules);
-
- AutoPlaceholderBatch treatAsOneTransaction(*this);
- AutoEditSubActionNotifier startToHandleEditSubAction(
- *this, EditSubAction::eSetOrClearAlignment, nsIEditor::eNext);
-
- bool cancel, handled;
-
- // Find out if the selection is collapsed:
- EditSubActionInfo subActionInfo(EditSubAction::eSetOrClearAlignment);
- subActionInfo.alignType = &aAlignType;
- nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
- if (cancel || NS_FAILED(rv)) {
- return EditorBase::ToGenericNSResult(rv);
- }
-
- rv = rules->DidDoAction(subActionInfo, rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return EditorBase::ToGenericNSResult(rv);
- }
- return NS_OK;
+ EditActionResult result = AlignAsSubAction(aAlignType);
+ NS_WARNING_ASSERTION(result.Succeeded(), "AlignAsSubAction() failed");
+ return EditorBase::ToGenericNSResult(result.Rv());
}
Element* HTMLEditor::GetElementOrParentByTagName(const nsAtom& aTagName,
diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h
index ac869558198d0..9a27baf2cf718 100644
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -259,8 +259,8 @@ class HTMLEditor final : public TextEditor,
MOZ_CAN_RUN_SCRIPT nsresult SetParagraphFormatAsAction(
const nsAString& aParagraphFormat, nsIPrincipal* aPrincipal = nullptr);
- nsresult AlignAsAction(const nsAString& aAlignType,
- nsIPrincipal* aPrincipal = nullptr);
+ MOZ_CAN_RUN_SCRIPT nsresult AlignAsAction(const nsAString& aAlignType,
+ nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult RemoveListAsAction(
const nsAString& aListType, nsIPrincipal* aPrincipal = nullptr);
@@ -2501,6 +2501,116 @@ class HTMLEditor final : public TextEditor,
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult RemoveAlignFromDescendants(
Element& aElement, const nsAString& aAlignType, EditTarget aEditTarget);
+ /**
+ * SetBlockElementAlign() resets `align` attribute, `text-align` property
+ * of descendants of aBlockOrHRElement except `
` element descendants.
+ * Then, set `align` attribute or `text-align` property of aBlockOrHRElement.
+ *
+ * @param aBlockOrHRElement The element whose contents will be aligned.
+ * This must be a block element or `` element.
+ * If we're not in CSS mode, this element has
+ * to support `align` attribute (i.e.,
+ * `HTMLEditUtils::SupportsAlignAttr()` must
+ * return true).
+ * @param aAlignType Boundary or "center" which contents should be
+ * aligned on.
+ * @param aEditTarget If `OnlyDescendantsExceptTable`, modifies only
+ * descendants of aBlockOrHRElement.
+ * If `NodeAndDescendantsExceptTable`, modifies
+ * aBlockOrHRElement and its descendants.
+ */
+ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+ SetBlockElementAlign(Element& aBlockOrHRElement, const nsAString& aAlignType,
+ EditTarget aEditTarget);
+
+ /**
+ * AlignContentsAtSelectionWithEmptyDivElement() inserts new `
` element
+ * at `Selection` to align selected contents. This returns as "handled"
+ * if this modifies `Selection` so that callers shouldn't modify `Selection`
+ * in such case especially when using AutoSelectionRestorer.
+ *
+ * @param aAlignType New align attribute value where the contents
+ * should be aligned to.
+ */
+ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult
+ AlignContentsAtSelectionWithEmptyDivElement(const nsAString& aAlignType);
+
+ /**
+ * AlignNodesAndDescendants() make contents of nodes in aArrayOfNodes and
+ * their descendants aligned to aAlignType.
+ *
+ * @param aAlignType New align attribute value where the contents
+ * should be aligned to.
+ */
+ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+ AlignNodesAndDescendants(nsTArray>& aArrayOfNodes,
+ const nsAString& aAlignType);
+
+ /**
+ * AlignContentsAtSelection() aligns contents around Selection to aAlignType.
+ * This creates AutoSelectionRestorer. Therefore, even if this returns
+ * NS_OK, we might have been destroyed. So, every caller needs to check if
+ * Destroyed() returns false before modifying the DOM tree or changing
+ * Selection.
+ * NOTE: Call AlignAsSubAction() instead.
+ *
+ * @param aAlignType New align attribute value where the contents
+ * should be aligned to.
+ */
+ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+ AlignContentsAtSelection(const nsAString& aAlignType);
+
+ /**
+ * AlignAsSubAction() handles "align" command with `Selection`.
+ *
+ * @param aAlignType New align attribute value where the contents
+ * should be aligned to.
+ */
+ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult
+ AlignAsSubAction(const nsAString& aAlignType);
+
+ /**
+ * StartOrEndOfSelectionRangesIsIn() returns true if start or end of one
+ * of selection ranges is in aContent.
+ */
+ bool StartOrEndOfSelectionRangesIsIn(nsIContent& aContent) const;
+
+ /**
+ * FindNearEditableContent() tries to find an editable node near aPoint.
+ *
+ * @param aPoint The DOM point where to start to search from.
+ * @param aDirection If nsIEditor::ePrevious is set, this searches an
+ * editable node from next nodes. Otherwise, from
+ * previous nodes.
+ * @return If found, returns non-nullptr. Otherwise, nullptr.
+ * Note that if found node is in different table element,
+ * this returns nullptr.
+ * And also if aDirection is not nsIEditor::ePrevious,
+ * the result may be the node pointed by aPoint.
+ */
+ template
+ nsIContent* FindNearEditableContent(const EditorDOMPointBase& aPoint,
+ nsIEditor::EDirection aDirection);
+
+ /**
+ * AdjustCaretPositionAndEnsurePaddingBRElement() may adjust caret
+ * position to nearest editable content and if padding ` ` element is
+ * necessary at caret position, this creates it.
+ *
+ * @param aDirectionAndAmount Direction of the edit action.
+ */
+ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+ AdjustCaretPositionAndEnsurePaddingBRElement(
+ nsIEditor::EDirection aDirectionAndAmount);
+
+ /**
+ * EnsureSelectionInBodyOrDocumentElement() collapse `Selection` to the
+ * primary `` element or document element when `Selection` crosses
+ * `` element's boundary.
+ */
+ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+ EnsureSelectionInBodyOrDocumentElement();
+
protected: // Called by helper classes.
virtual void OnStartToHandleTopLevelEditSubAction(
EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;
diff --git a/editor/nsIHTMLEditor.idl b/editor/nsIHTMLEditor.idl
index 2aba22a346d88..d0d650317b3ba 100644
--- a/editor/nsIHTMLEditor.idl
+++ b/editor/nsIHTMLEditor.idl
@@ -307,6 +307,7 @@ interface nsIHTMLEditor : nsISupports
* Document me!
*
*/
+ [can_run_script]
void align(in AString aAlign);
/**
diff --git a/gfx/wr/Cargo.lock b/gfx/wr/Cargo.lock
index af7a494d4ea0d..b9f38edc437d8 100644
--- a/gfx/wr/Cargo.lock
+++ b/gfx/wr/Cargo.lock
@@ -174,6 +174,9 @@ dependencies = [
name = "cc"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
name = "cfg-if"
@@ -420,7 +423,7 @@ version = "0.1.0"
dependencies = [
"euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "mozangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mozangle 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender 0.60.0",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"winit 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -592,17 +595,17 @@ dependencies = [
[[package]]
name = "gl_generator"
-version = "0.9.0"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "khronos_api 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gl_generator"
-version = "0.11.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -781,11 +784,6 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "khronos_api"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
[[package]]
name = "khronos_api"
version = "3.1.0"
@@ -939,12 +937,13 @@ dependencies = [
[[package]]
name = "mozangle"
-version = "0.1.7"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1834,7 +1833,7 @@ dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "mozangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mozangle 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"plane-split 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"png 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1995,7 +1994,7 @@ dependencies = [
"image 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "mozangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mozangle 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"osmesa-src 0.1.1 (git+https://github.com/servo/osmesa-src)",
"osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2050,14 +2049,6 @@ name = "xdg"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-[[package]]
-name = "xml-rs"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
[[package]]
name = "xml-rs"
version = "0.8.0"
@@ -2148,7 +2139,7 @@ dependencies = [
"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592"
"checksum gif 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3414b424657317e708489d2857d9575f4403698428b040b609b9d1c1a84a2c"
"checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd"
-"checksum gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a"
+"checksum gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a"
"checksum gleam 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7f46fd8874e043ffac0d638ed1567a2584f7814f6d72b4db37ab1689004a26c4"
"checksum glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb26027a84c3b9e1949ef0df0b6a3db8d0c124243a5c161ea25c7def90cb1474"
"checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326"
@@ -2166,7 +2157,6 @@ dependencies = [
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
"checksum jpeg-decoder 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0dfe27a6c0dabd772d0f9b9f8701c4ca12c4d1eebcadf2be1f6f70396f6a1434"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum khronos_api 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "037ab472c33f67b5fbd3e9163a2645319e5356fcd355efa6d4eb7fff4bbcb554"
"checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
@@ -2187,7 +2177,7 @@ dependencies = [
"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432"
"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
-"checksum mozangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "45a8a18a41cfab0fde25cc2f43ea89064d211a0fbb33225b8ff93ab20406e0e7"
+"checksum mozangle 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "75a61b5a06b6f362eb45590ddf2643c255768a7039bcde1dc70320b97e7f9651"
"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0"
"checksum nix 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "319fffb13b63c0f4ff5a4e1c97566e7e741561ff5d03bf8bbf11653454bbd70b"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
@@ -2301,7 +2291,6 @@ dependencies = [
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)" = "940586acb859ea05c53971ac231685799a7ec1dee66ac0bccc0e6ad96e06b4e3"
"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
-"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2"
"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"
"checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"
"checksum yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e"
diff --git a/gfx/wr/direct-composition/Cargo.toml b/gfx/wr/direct-composition/Cargo.toml
index d21934a36c7bf..ce8d598b002e9 100644
--- a/gfx/wr/direct-composition/Cargo.toml
+++ b/gfx/wr/direct-composition/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2018"
[target.'cfg(windows)'.dependencies]
euclid = "0.20"
gleam = "0.6.2"
-mozangle = {version = "0.1", features = ["egl"]}
+mozangle = {version = "0.2.7", features = ["egl"]}
webrender = {path = "../webrender"}
winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]}
winit = "0.19"
diff --git a/gfx/wr/servo-tidy.toml b/gfx/wr/servo-tidy.toml
index 13189aa7ba287..30bfde55233df 100644
--- a/gfx/wr/servo-tidy.toml
+++ b/gfx/wr/servo-tidy.toml
@@ -8,7 +8,6 @@ check-alphabetical-order = false
packages = [
"crossbeam-utils",
"gl_generator",
- "khronos_api",
"lazy_static",
"nix",
"percent-encoding",
@@ -17,7 +16,6 @@ packages = [
"winapi",
"core-graphics",
"core-text",
- "xml-rs",
"yaml-rust",
]
diff --git a/gfx/wr/webrender/Cargo.toml b/gfx/wr/webrender/Cargo.toml
index cde7c0f582a67..f7abc1713b404 100644
--- a/gfx/wr/webrender/Cargo.toml
+++ b/gfx/wr/webrender/Cargo.toml
@@ -54,7 +54,7 @@ ws = { optional = true, version = "0.9" }
svg_fmt = "0.4"
[dev-dependencies]
-mozangle = "0.1"
+mozangle = "0.2"
rand = "0.4"
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
diff --git a/gfx/wr/wrench/Cargo.toml b/gfx/wr/wrench/Cargo.toml
index a0424ef129daa..60be1bdcc417a 100644
--- a/gfx/wr/wrench/Cargo.toml
+++ b/gfx/wr/wrench/Cargo.toml
@@ -42,7 +42,7 @@ headless = [ "osmesa-sys", "osmesa-src" ]
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.9"
-mozangle = {version = "0.1.5", features = ["egl"]}
+mozangle = {version = "0.2", features = ["egl"]}
[target.'cfg(all(unix, not(target_os = "android")))'.dependencies]
font-loader = "0.7"
diff --git a/layout/generic/crashtests/1248227.html b/layout/generic/crashtests/1248227.html
new file mode 100644
index 0000000000000..2b2f3ff982492
--- /dev/null
+++ b/layout/generic/crashtests/1248227.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+