mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-03 02:25:34 +00:00
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
commit
39d9b048b9
@ -802,7 +802,7 @@ nsAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString)
|
||||
#define ROLE(geckoRole, stringRole, atkRole, \
|
||||
macRole, msaaRole, ia2Role, nameRule) \
|
||||
case roles::geckoRole: \
|
||||
CopyUTF8toUTF16(stringRole, aString); \
|
||||
aString.AssignLiteral(stringRole); \
|
||||
return;
|
||||
|
||||
switch (aRole) {
|
||||
@ -992,7 +992,7 @@ nsAccessibilityService::GetStringEventType(uint32_t aEventType,
|
||||
return;
|
||||
}
|
||||
|
||||
CopyUTF8toUTF16(kEventTypeNames[aEventType], aString);
|
||||
aString.AssignASCII(kEventTypeNames[aEventType]);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -460,7 +460,15 @@ pref("browser.link.open_newwindow.disabled_in_fullscreen", false);
|
||||
// Tabbed browser
|
||||
pref("browser.tabs.closeTabByDblclick", false);
|
||||
pref("browser.tabs.closeWindowWithLastTab", true);
|
||||
// Open related links to a tab, e.g., link in current tab, at next to the
|
||||
// current tab if |insertRelatedAfterCurrent| is true. Otherwise, always
|
||||
// append new tab to the end.
|
||||
pref("browser.tabs.insertRelatedAfterCurrent", true);
|
||||
// Open all links, e.g., bookmarks, history items at next to current tab
|
||||
// if |insertAfterCurrent| is true. Otherwise, append new tab to the end
|
||||
// for non-related links. Note that if this is set to true, it will trump
|
||||
// the value of browser.tabs.insertRelatedAfterCurrent.
|
||||
pref("browser.tabs.insertAfterCurrent", false);
|
||||
pref("browser.tabs.warnOnClose", true);
|
||||
pref("browser.tabs.warnOnCloseOtherTabs", true);
|
||||
pref("browser.tabs.warnOnOpen", true);
|
||||
@ -1731,10 +1739,6 @@ pref("browser.chrome.errorReporter.logLevel", "Error");
|
||||
pref("browser.chrome.errorReporter.infoURL",
|
||||
"https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/nightly-error-collection");
|
||||
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
pref("browser.policies.enabled", true);
|
||||
#endif
|
||||
|
||||
// Normandy client preferences
|
||||
pref("app.normandy.api_url", "https://normandy.cdn.mozilla.net/api/v1");
|
||||
pref("app.normandy.dev_mode", false);
|
||||
|
@ -53,9 +53,7 @@ var gSync = {
|
||||
// if any remote clients exist.
|
||||
get syncConfiguredAndLoading() {
|
||||
return UIState.get().status == UIState.STATUS_SIGNED_IN &&
|
||||
(!this.syncReady ||
|
||||
// lastSync will be non-zero after the first sync
|
||||
Weave.Service.clientsEngine.lastSync == 0);
|
||||
(!this.syncReady || Weave.Service.clientsEngine.isFirstSync);
|
||||
},
|
||||
|
||||
get isSignedIn() {
|
||||
|
@ -27,8 +27,6 @@
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
buttons="accept,cancel"
|
||||
persist="lastSelected screenX screenY"
|
||||
closebuttonlabel="&preferencesCloseButton.label;"
|
||||
closebuttonaccesskey="&preferencesCloseButton.accesskey;"
|
||||
role="dialog"
|
||||
title="&sanitizeDialog2.title;"
|
||||
noneverythingtitle="&sanitizeDialog2.title;"
|
||||
|
@ -2145,6 +2145,7 @@ window._gBrowser = {
|
||||
var aNoInitialLabel;
|
||||
var aFocusUrlBar;
|
||||
var aName;
|
||||
var aBulkOrderedOpen;
|
||||
if (arguments.length == 2 &&
|
||||
typeof arguments[1] == "object" &&
|
||||
!(arguments[1] instanceof Ci.nsIURI)) {
|
||||
@ -2176,6 +2177,7 @@ window._gBrowser = {
|
||||
aNoInitialLabel = params.noInitialLabel;
|
||||
aFocusUrlBar = params.focusUrlBar;
|
||||
aName = params.name;
|
||||
aBulkOrderedOpen = params.bulkOrderedOpen;
|
||||
}
|
||||
|
||||
// if we're adding tabs, we're past interrupt mode, ditch the owner
|
||||
@ -2428,19 +2430,22 @@ window._gBrowser = {
|
||||
}
|
||||
}
|
||||
|
||||
// If we're opening a tab related to the an existing tab, move it
|
||||
// to a position after that tab.
|
||||
if (openerTab &&
|
||||
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
|
||||
// Move the new tab after another tab if needed.
|
||||
if (!aBulkOrderedOpen &&
|
||||
((openerTab &&
|
||||
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) ||
|
||||
Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent"))) {
|
||||
|
||||
let lastRelatedTab = openerTab && this._lastRelatedTabMap.get(openerTab);
|
||||
let newTabPos = (lastRelatedTab || openerTab || this.selectedTab)._tPos + 1;
|
||||
|
||||
let lastRelatedTab = this._lastRelatedTabMap.get(openerTab);
|
||||
let newTabPos = (lastRelatedTab || openerTab)._tPos + 1;
|
||||
if (lastRelatedTab)
|
||||
lastRelatedTab.owner = null;
|
||||
else
|
||||
else if (openerTab)
|
||||
t.owner = openerTab;
|
||||
this.moveTabTo(t, newTabPos, true);
|
||||
this._lastRelatedTabMap.set(openerTab, t);
|
||||
if (openerTab)
|
||||
this._lastRelatedTabMap.set(openerTab, t);
|
||||
}
|
||||
|
||||
// This field is updated regardless if we actually animate
|
||||
|
@ -17,7 +17,7 @@ function promiseSyncReady() {
|
||||
function setupSendTabMocks({ syncReady, clientsSynced, remoteClients, state, isSendableURI }) {
|
||||
const sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(gSync, "syncReady").get(() => syncReady);
|
||||
sandbox.stub(Weave.Service.clientsEngine, "lastSync").get(() => clientsSynced ? Date.now() : 0);
|
||||
sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => !clientsSynced);
|
||||
sandbox.stub(gSync, "remoteClients").get(() => remoteClients);
|
||||
sandbox.stub(UIState, "get").returns({ status: state });
|
||||
sandbox.stub(gSync, "isSendableURI").returns(isSendableURI);
|
||||
|
@ -24,6 +24,8 @@ skip-if = !e10s # Test only relevant for e10s.
|
||||
skip-if = !e10s # Pref and test only relevant for e10s.
|
||||
[browser_newwindow_tabstrip_overflow.js]
|
||||
[browser_opened_file_tab_navigated_to_web.js]
|
||||
[browser_new_tab_insert_position.js]
|
||||
support-files = file_new_tab_page.html
|
||||
[browser_overflowScroll.js]
|
||||
[browser_pinnedTabs.js]
|
||||
[browser_pinnedTabs_closeByKeyboard.js]
|
||||
|
@ -0,0 +1,177 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "TabStateFlusher",
|
||||
"resource:///modules/sessionstore/TabStateFlusher.jsm");
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/sessionstore/Utils.jsm", this);
|
||||
const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL;
|
||||
|
||||
function promiseBrowserStateRestored(state) {
|
||||
if (typeof state != "string") {
|
||||
state = JSON.stringify(state);
|
||||
}
|
||||
// We wait for the notification that restore is done, and for the notification
|
||||
// that the active tab is loaded and restored.
|
||||
let promise = Promise.all([
|
||||
TestUtils.topicObserved("sessionstore-browser-state-restored"),
|
||||
BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "SSTabRestored")
|
||||
]);
|
||||
SessionStore.setBrowserState(state);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function promiseRemoveThenUndoCloseTab(tab) {
|
||||
// We wait for the notification that restore is done, and for the notification
|
||||
// that the active tab is loaded and restored.
|
||||
let promise = Promise.all([
|
||||
TestUtils.topicObserved("sessionstore-closed-objects-changed"),
|
||||
BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "SSTabRestored")
|
||||
]);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
SessionStore.undoCloseTab(window, 0);
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Compare the current browser tab order against the session state ordering, they should always match.
|
||||
function verifyTabState(state) {
|
||||
let newStateTabs = JSON.parse(state).windows[0].tabs;
|
||||
for (let i = 0; i < gBrowser.tabs.length; i++) {
|
||||
is(gBrowser.tabs[i].linkedBrowser.currentURI.spec, newStateTabs[i].entries[0].url, `tab pos ${i} matched ${gBrowser.tabs[i].linkedBrowser.currentURI.spec}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function doTest(aInsertRelatedAfterCurrent, aInsertAfterCurrent) {
|
||||
const kDescription = "(aInsertRelatedAfterCurrent=" + aInsertRelatedAfterCurrent +
|
||||
", aInsertAfterCurrent=" + aInsertAfterCurrent + "): ";
|
||||
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
["browser.tabs.opentabfor.middleclick", true],
|
||||
["browser.tabs.loadBookmarksInBackground", false],
|
||||
["browser.tabs.insertRelatedAfterCurrent", aInsertRelatedAfterCurrent],
|
||||
["browser.tabs.insertAfterCurrent", aInsertAfterCurrent],
|
||||
["browser.sessionstore.restore_tabs_lazily", false],
|
||||
]});
|
||||
|
||||
let oldState = SessionStore.getBrowserState();
|
||||
|
||||
let sessData = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{entries: [{url: "http://mochi.test:8888/#0", triggeringPrincipal_base64}]},
|
||||
{entries: [{url: "http://mochi.test:8888/#1", triggeringPrincipal_base64}]},
|
||||
{entries: [{url: "http://mochi.test:8888/#3", triggeringPrincipal_base64}]},
|
||||
{entries: [{url: "http://mochi.test:8888/#4", triggeringPrincipal_base64}]},
|
||||
],
|
||||
}],
|
||||
};
|
||||
|
||||
await promiseBrowserStateRestored(sessData);
|
||||
|
||||
// Create a *opener* tab page which has a link to "example.com".
|
||||
let pageURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
|
||||
pageURL = `${pageURL}file_new_tab_page.html`;
|
||||
let openerTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageURL);
|
||||
const openerTabIndex = 1;
|
||||
gBrowser.moveTabTo(openerTab, openerTabIndex);
|
||||
|
||||
// Open a related tab via Middle click on the cell and test its position.
|
||||
let openTabIndex = aInsertRelatedAfterCurrent || aInsertAfterCurrent ?
|
||||
openerTabIndex + 1 : gBrowser.tabs.length;
|
||||
let openTabDescription = aInsertRelatedAfterCurrent || aInsertAfterCurrent ?
|
||||
"immediately to the right" : "at rightmost";
|
||||
|
||||
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/#linkclick", true);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#link_to_example_com",
|
||||
{button: 1}, gBrowser.selectedBrowser);
|
||||
let openTab = await newTabPromise;
|
||||
is(openTab.linkedBrowser.currentURI.spec, "http://example.com/#linkclick",
|
||||
"Middle click should open site to correct url.");
|
||||
is(openTab._tPos, openTabIndex,
|
||||
kDescription + "Middle click should open site in a new tab " + openTabDescription);
|
||||
if (aInsertRelatedAfterCurrent || aInsertAfterCurrent) {
|
||||
is(openTab.owner, openerTab, "tab owner is set correctly");
|
||||
}
|
||||
is(openTab.openerTab, openerTab, "opener tab is set");
|
||||
|
||||
// Open an unrelated tab from the URL bar and test its position.
|
||||
openTabIndex = aInsertAfterCurrent ? openerTabIndex + 1 : gBrowser.tabs.length;
|
||||
openTabDescription = aInsertAfterCurrent ? "immediately to the right" : "at rightmost";
|
||||
|
||||
const urlbarURL = "http://example.com/#urlbar";
|
||||
gURLBar.focus();
|
||||
gURLBar.select();
|
||||
newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, urlbarURL, true);
|
||||
EventUtils.sendString(urlbarURL);
|
||||
EventUtils.synthesizeKey("KEY_Alt", { altKey: true, code: "AltLeft", type: "keydown" });
|
||||
EventUtils.synthesizeKey("KEY_Enter", { altKey: true, code: "Enter" });
|
||||
EventUtils.synthesizeKey("KEY_Alt", { altKey: false, code: "AltLeft", type: "keyup" });
|
||||
let unrelatedTab = await newTabPromise;
|
||||
|
||||
is(gBrowser.selectedBrowser.currentURI.spec, unrelatedTab.linkedBrowser.currentURI.spec,
|
||||
`${kDescription} ${urlbarURL} should be loaded in the current tab.`);
|
||||
is(unrelatedTab._tPos, openTabIndex,
|
||||
`${kDescription} Alt+Enter in the URL bar should open page in a new tab ${openTabDescription}`);
|
||||
is(unrelatedTab.owner, openerTab, "owner tab is set correctly");
|
||||
ok(!unrelatedTab.openerTab, "no opener tab is set");
|
||||
|
||||
// Closing this should go back to the last selected tab, which just happens to be "openerTab"
|
||||
// but is not in fact the opener.
|
||||
BrowserTestUtils.removeTab(unrelatedTab);
|
||||
is(gBrowser.selectedTab, openerTab,
|
||||
kDescription + `openerTab should be selected after closing unrelated tab`);
|
||||
|
||||
// Go back to the opener tab. Closing the child tab should return to the opener.
|
||||
BrowserTestUtils.removeTab(openTab);
|
||||
is(gBrowser.selectedTab, openerTab,
|
||||
kDescription + "openerTab should be selected after closing related tab");
|
||||
|
||||
// Flush before messing with browser state.
|
||||
for (let tab of gBrowser.tabs) {
|
||||
await TabStateFlusher.flush(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
// Get the session state, verify SessionStore gives us expected data.
|
||||
let newState = SessionStore.getBrowserState();
|
||||
verifyTabState(newState);
|
||||
|
||||
// Remove the tab at the end, then undo. It should reappear where it was.
|
||||
await promiseRemoveThenUndoCloseTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
verifyTabState(newState);
|
||||
|
||||
// Remove a tab in the middle, then undo. It should reappear where it was.
|
||||
await promiseRemoveThenUndoCloseTab(gBrowser.tabs[2]);
|
||||
verifyTabState(newState);
|
||||
|
||||
// Now we want to test that positioning remains correct after a session restore.
|
||||
|
||||
// Restore pre-test state so we can restore and test tab ordering.
|
||||
await promiseBrowserStateRestored(oldState);
|
||||
|
||||
// Restore test state and verify it is as it was.
|
||||
await promiseBrowserStateRestored(newState);
|
||||
verifyTabState(newState);
|
||||
|
||||
// Restore pre-test state for next test.
|
||||
await promiseBrowserStateRestored(oldState);
|
||||
}
|
||||
|
||||
add_task(async function test_settings_insertRelatedAfter() {
|
||||
// Firefox default settings.
|
||||
await doTest(true, false);
|
||||
});
|
||||
|
||||
add_task(async function test_settings_insertAfter() {
|
||||
await doTest(true, true);
|
||||
});
|
||||
|
||||
add_task(async function test_settings_always_insertAfter() {
|
||||
await doTest(false, true);
|
||||
});
|
||||
|
||||
add_task(async function test_settings_always_insertAtEnd() {
|
||||
await doTest(false, false);
|
||||
});
|
9
browser/base/content/test/tabs/file_new_tab_page.html
Normal file
9
browser/base/content/test/tabs/file_new_tab_page.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<a href="http://example.com/#linkclick" id="link_to_example_com">go to example.com</a>
|
||||
</body>
|
||||
</html>
|
@ -213,7 +213,7 @@ add_task(async function sendToDevice_syncNotReady_other_states() {
|
||||
await promiseSyncReady();
|
||||
const sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(gSync, "syncReady").get(() => false);
|
||||
sandbox.stub(Weave.Service.clientsEngine, "lastSync").get(() => 0);
|
||||
sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => true);
|
||||
sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_NOT_VERIFIED });
|
||||
sandbox.stub(gSync, "isSendableURI").returns(true);
|
||||
|
||||
@ -270,7 +270,7 @@ add_task(async function sendToDevice_syncNotReady_configured() {
|
||||
await promiseSyncReady();
|
||||
const sandbox = sinon.sandbox.create();
|
||||
const syncReady = sandbox.stub(gSync, "syncReady").get(() => false);
|
||||
const lastSync = sandbox.stub(Weave.Service.clientsEngine, "lastSync").get(() => 0);
|
||||
const lastSync = sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => true);
|
||||
sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
|
||||
sandbox.stub(gSync, "isSendableURI").returns(true);
|
||||
|
||||
@ -410,7 +410,7 @@ add_task(async function sendToDevice_noDevices() {
|
||||
await promiseSyncReady();
|
||||
const sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(gSync, "syncReady").get(() => true);
|
||||
sandbox.stub(Weave.Service.clientsEngine, "lastSync").get(() => Date.now());
|
||||
sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => false);
|
||||
sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
|
||||
sandbox.stub(gSync, "isSendableURI").returns(true);
|
||||
sandbox.stub(gSync, "remoteClients").get(() => []);
|
||||
@ -475,7 +475,7 @@ add_task(async function sendToDevice_devices() {
|
||||
await promiseSyncReady();
|
||||
const sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(gSync, "syncReady").get(() => true);
|
||||
sandbox.stub(Weave.Service.clientsEngine, "lastSync").get(() => Date.now());
|
||||
sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => false);
|
||||
sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
|
||||
sandbox.stub(gSync, "isSendableURI").returns(true);
|
||||
sandbox.stub(gSync, "remoteClients").get(() => mockRemoteClients);
|
||||
@ -539,7 +539,7 @@ add_task(async function sendToDevice_inUrlbar() {
|
||||
await promiseSyncReady();
|
||||
const sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(gSync, "syncReady").get(() => true);
|
||||
sandbox.stub(Weave.Service.clientsEngine, "lastSync").get(() => Date.now());
|
||||
sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => false);
|
||||
sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
|
||||
sandbox.stub(gSync, "isSendableURI").returns(true);
|
||||
sandbox.stub(gSync, "remoteClients").get(() => mockRemoteClients);
|
||||
|
@ -27,10 +27,6 @@ const PREF_ALTERNATE_PATH = "browser.policies.alternatePath";
|
||||
const MAGIC_TEST_ROOT_PREFIX = "<test-root>";
|
||||
const PREF_TEST_ROOT = "mochitest.testRoot";
|
||||
|
||||
// This pref is meant to be temporary: it will only be used while we're
|
||||
// testing this feature without rolling it out officially. When the
|
||||
// policy engine is released, this pref should be removed.
|
||||
const PREF_ENABLED = "browser.policies.enabled";
|
||||
const PREF_LOGLEVEL = "browser.policies.loglevel";
|
||||
|
||||
// To force disallowing enterprise-only policies during tests
|
||||
@ -81,11 +77,6 @@ EnterprisePoliciesManager.prototype = {
|
||||
_xpcom_factory: EnterprisePoliciesFactory,
|
||||
|
||||
_initialize() {
|
||||
if (!Services.prefs.getBoolPref(PREF_ENABLED, false)) {
|
||||
this.status = Ci.nsIEnterprisePolicies.INACTIVE;
|
||||
return;
|
||||
}
|
||||
|
||||
let provider = this._chooseProvider();
|
||||
|
||||
if (!provider) {
|
||||
|
@ -349,6 +349,63 @@ var Policies = {
|
||||
setAndLockPref("signon.rememberSignons", param);
|
||||
}
|
||||
},
|
||||
|
||||
"SearchEngines": {
|
||||
onAllWindowsRestored(manager, param) {
|
||||
Services.search.init(() => {
|
||||
if (param.Add) {
|
||||
// Only rerun if the list of engine names has changed.
|
||||
let engineNameList = param.Add.map(engine => engine.Name);
|
||||
runOncePerModification("addSearchEngines",
|
||||
JSON.stringify(engineNameList),
|
||||
() => {
|
||||
for (let newEngine of param.Add) {
|
||||
let newEngineParameters = {
|
||||
template: newEngine.URLTemplate,
|
||||
iconURL: newEngine.IconURL,
|
||||
alias: newEngine.Alias,
|
||||
description: newEngine.Description,
|
||||
method: newEngine.Method,
|
||||
suggestURL: newEngine.SuggestURLTemplate,
|
||||
extensionID: "set-via-policy"
|
||||
};
|
||||
try {
|
||||
Services.search.addEngineWithDetails(newEngine.Name,
|
||||
newEngineParameters);
|
||||
} catch (ex) {
|
||||
log.error("Unable to add search engine", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (param.Default) {
|
||||
runOnce("setDefaultSearchEngine", () => {
|
||||
let defaultEngine;
|
||||
try {
|
||||
defaultEngine = Services.search.getEngineByName(param.Default);
|
||||
if (!defaultEngine) {
|
||||
throw "No engine by that name could be found";
|
||||
}
|
||||
} catch (ex) {
|
||||
log.error(`Search engine lookup failed when attempting to set ` +
|
||||
`the default engine. Requested engine was ` +
|
||||
`"${param.Default}".`, ex);
|
||||
}
|
||||
if (defaultEngine) {
|
||||
try {
|
||||
Services.search.currentEngine = defaultEngine;
|
||||
} catch (ex) {
|
||||
log.error("Unable to set the default search engine", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (param.PreventInstalls) {
|
||||
manager.disallowFeature("installSearchEngine");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -367,6 +367,54 @@
|
||||
"first_available": "60.0",
|
||||
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
"SearchEngines": {
|
||||
"description": "Modifies the list of search engines built into Firefox",
|
||||
"first_available": "60.0",
|
||||
"enterprise_only": true,
|
||||
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Add": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["Name", "URLTemplate"],
|
||||
|
||||
"properties": {
|
||||
"Name": {
|
||||
"type": "string"
|
||||
},
|
||||
"IconURL": {
|
||||
"type": "URLorEmpty"
|
||||
},
|
||||
"Alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"Description": {
|
||||
"type": "string"
|
||||
},
|
||||
"Method": {
|
||||
"type": "string",
|
||||
"enum": ["GET", "POST"]
|
||||
},
|
||||
"URLTemplate": {
|
||||
"type": "string"
|
||||
},
|
||||
"SuggestURLTemplate": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Default": {
|
||||
"type": "string"
|
||||
},
|
||||
"PreventInstalls": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
browser.policies.enabled=true
|
||||
support-files =
|
||||
head.js
|
||||
config_popups_cookies_addons_flash.json
|
||||
config_broken_json.json
|
||||
opensearch.html
|
||||
opensearchEngine.xml
|
||||
|
||||
[browser_policies_basic_tests.js]
|
||||
[browser_policies_broken_json.js]
|
||||
@ -36,4 +36,5 @@ support-files =
|
||||
[browser_policy_display_bookmarks.js]
|
||||
[browser_policy_display_menu.js]
|
||||
[browser_policy_proxy.js]
|
||||
[browser_policy_search_engine.js]
|
||||
[browser_policy_set_homepage.js]
|
||||
|
@ -0,0 +1,149 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("browser.policies.runonce.setDefaultSearchEngine");
|
||||
Services.prefs.clearUserPref("browser.policies.runOncePerModification.addSearchEngines");
|
||||
});
|
||||
|
||||
// |shouldWork| should be true if opensearch is expected to work and false if
|
||||
// it is not.
|
||||
async function test_opensearch(shouldWork) {
|
||||
await SpecialPowers.pushPrefEnv({ set: [
|
||||
["browser.search.widget.inNavBar", true],
|
||||
]});
|
||||
let rootDir = getRootDirectory(gTestPath);
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, rootDir + "opensearch.html");
|
||||
let searchPopup = document.getElementById("PopupSearchAutoComplete");
|
||||
let searchBar = document.getElementById("searchbar");
|
||||
let promiseSearchPopupShown = BrowserTestUtils.waitForEvent(searchPopup, "popupshown");
|
||||
let searchBarButton = document.getAnonymousElementByAttribute(searchBar,
|
||||
"anonid",
|
||||
"searchbar-search-button");
|
||||
searchBarButton.click();
|
||||
await promiseSearchPopupShown;
|
||||
let oneOffsContainer = document.getAnonymousElementByAttribute(searchPopup,
|
||||
"anonid",
|
||||
"search-one-off-buttons");
|
||||
let engineListElement = document.getAnonymousElementByAttribute(oneOffsContainer,
|
||||
"anonid",
|
||||
"add-engines");
|
||||
if (shouldWork) {
|
||||
ok(engineListElement.firstChild,
|
||||
"There should be search engines available to add");
|
||||
ok(searchBar.getAttribute("addengines"),
|
||||
"Search bar should have addengines attribute");
|
||||
} else {
|
||||
is(engineListElement.firstChild, null,
|
||||
"There should be no search engines available to add");
|
||||
ok(!searchBar.getAttribute("addengines"),
|
||||
"Search bar should not have addengines attribute");
|
||||
}
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
add_task(async function test_install_and_set_default() {
|
||||
// Make sure we are starting in an expected state to avoid false positive
|
||||
// test results.
|
||||
isnot(Services.search.currentEngine.name, "MozSearch",
|
||||
"Default search engine should not be MozSearch when test starts");
|
||||
is(Services.search.getEngineByName("Foo"), null,
|
||||
"Engine \"Foo\" should not be present when test starts");
|
||||
|
||||
await setupPolicyEngineWithJson({
|
||||
"policies": {
|
||||
"SearchEngines": {
|
||||
"Add": [
|
||||
{
|
||||
"Name": "MozSearch",
|
||||
"URLTemplate": "http://example.com/?q={searchTerms}"
|
||||
}
|
||||
],
|
||||
"Default": "MozSearch"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If this passes, it means that the new search engine was properly installed
|
||||
// *and* was properly set as the default.
|
||||
is(Services.search.currentEngine.name, "MozSearch",
|
||||
"Specified search engine should be the default");
|
||||
|
||||
// Clean up
|
||||
Services.search.removeEngine(Services.search.currentEngine);
|
||||
});
|
||||
|
||||
add_task(async function test_opensearch_works() {
|
||||
// Ensure that opensearch works before we make sure that it can be properly
|
||||
// disabled
|
||||
await test_opensearch(true);
|
||||
});
|
||||
|
||||
add_task(async function setup_prevent_installs() {
|
||||
await setupPolicyEngineWithJson({
|
||||
"policies": {
|
||||
"SearchEngines": {
|
||||
"PreventInstalls": true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_prevent_install_ui() {
|
||||
// Check that about:preferences does not prompt user to install search engines
|
||||
// if that feature is disabled
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
|
||||
let linkContainer = content.document.getElementById("addEnginesBox");
|
||||
if (!linkContainer.hidden) {
|
||||
await new Promise(resolve => {
|
||||
let mut = new linkContainer.ownerGlobal.MutationObserver(mutations => {
|
||||
mut.disconnect();
|
||||
resolve();
|
||||
});
|
||||
mut.observe(linkContainer, {attributeFilter: ["hidden"]});
|
||||
});
|
||||
}
|
||||
is(linkContainer.hidden, true,
|
||||
"\"Find more search engines\" link should be hidden");
|
||||
});
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_opensearch_disabled() {
|
||||
// Check that search engines cannot be added via opensearch
|
||||
await test_opensearch(false);
|
||||
});
|
||||
|
||||
add_task(async function test_AddSearchProvider() {
|
||||
// Mock the modal error dialog
|
||||
let mockPrompter = {
|
||||
promptCount: 0,
|
||||
alert() {
|
||||
this.promptCount++;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
|
||||
};
|
||||
let windowWatcher = {
|
||||
getNewPrompter: () => mockPrompter,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher]),
|
||||
};
|
||||
let origWindowWatcher = Services.ww;
|
||||
Services.ww = windowWatcher;
|
||||
registerCleanupFunction(() => {
|
||||
Services.ww = origWindowWatcher;
|
||||
});
|
||||
|
||||
let engineURL = getRootDirectory(gTestPath) + "opensearchEngine.xml";
|
||||
// AddSearchProvider will refuse to take URLs with a "chrome:" scheme
|
||||
engineURL = engineURL.replace("chrome://mochitests/content", "http://example.com");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {engineURL}, async function(args) {
|
||||
content.window.external.AddSearchProvider(args.engineURL);
|
||||
});
|
||||
|
||||
is(Services.search.getEngineByName("Foo"), null,
|
||||
"Engine should not have been added successfully.");
|
||||
is(mockPrompter.promptCount, 1,
|
||||
"Should have alerted the user of an error when installing new search engine");
|
||||
});
|
@ -2,7 +2,6 @@
|
||||
prefs =
|
||||
app.update.enabled=true
|
||||
app.update.auto=true
|
||||
browser.policies.enabled=true
|
||||
browser.policies.alternatePath='<test-root>/browser/components/enterprisepolicies/tests/browser/disable_app_update/config_disable_app_update.json'
|
||||
support-files =
|
||||
config_disable_app_update.json
|
||||
|
@ -1,6 +1,5 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
browser.policies.enabled=true
|
||||
browser.policies.alternatePath='<test-root>/browser/components/enterprisepolicies/tests/browser/disable_default_bookmarks/bookmarks_policies.json'
|
||||
support-files =
|
||||
bookmarks_policies.json
|
||||
|
@ -1,6 +1,5 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
browser.policies.enabled=true
|
||||
browser.policies.alternatePath='<test-root>/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/config_disable_developer_tools.json'
|
||||
support-files =
|
||||
config_disable_developer_tools.json
|
||||
|
@ -1,6 +1,5 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
browser.policies.enabled=true
|
||||
browser.policies.alternatePath='<test-root>/browser/components/enterprisepolicies/tests/browser/disable_fxscreenshots/config_disable_fxscreenshots.json'
|
||||
extensions.screenshots.disabled=false
|
||||
support-files =
|
||||
|
@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="newEngine" href="http://mochi.test:8888/browser/browser/components/enterprisepolicies/tests/browser/opensearchEngine.xml">
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
@ -0,0 +1,12 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
|
||||
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
|
||||
<ShortName>Foo</ShortName>
|
||||
<Description>Foo Search</Description>
|
||||
<InputEncoding>utf-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC</Image>
|
||||
<Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/?search">
|
||||
<Param name="test" value="{searchTerms}"/>
|
||||
</Url>
|
||||
<moz:SearchForm>http://mochi.test:8888/browser/browser/components/search/test/</moz:SearchForm>
|
||||
<moz:Alias>fooalias</moz:Alias>
|
||||
</OpenSearchDescription>
|
@ -20,8 +20,6 @@
|
||||
title="&colorsDialog.title;"
|
||||
buttons="accept,cancel,help"
|
||||
persist="lastSelected screenX screenY"
|
||||
closebuttonlabel="&preferencesCloseButton.label;"
|
||||
closebuttonaccesskey="&preferencesCloseButton.accesskey;"
|
||||
role="dialog"
|
||||
helpTopic="prefs-fonts-and-colors"
|
||||
ondialoghelp="openPrefsHelp()"
|
||||
|
@ -21,8 +21,6 @@
|
||||
title="&connectionsDialog.title;"
|
||||
buttons="accept,cancel,help"
|
||||
persist="lastSelected screenX screenY"
|
||||
closebuttonlabel="&preferencesCloseButton.label;"
|
||||
closebuttonaccesskey="&preferencesCloseButton.accesskey;"
|
||||
role="dialog"
|
||||
onbeforeaccept="return gConnectionsDialog.beforeAccept();"
|
||||
onload="gConnectionsDialog.checkForSystemProxy();"
|
||||
|
@ -20,8 +20,6 @@
|
||||
title="&fontsDialog.title;"
|
||||
buttons="accept,cancel,help"
|
||||
persist="lastSelected screenX screenY"
|
||||
closebuttonlabel="&preferencesCloseButton.label;"
|
||||
closebuttonaccesskey="&preferencesCloseButton.accesskey;"
|
||||
role="dialog"
|
||||
helpTopic="prefs-fonts-and-colors"
|
||||
ondialoghelp="openPrefsHelp()"
|
||||
|
@ -40,10 +40,15 @@ var gSearchPane = {
|
||||
document.getElementById("engineList").view = gEngineView;
|
||||
this.buildDefaultEngineDropDown();
|
||||
|
||||
let addEnginesLink = document.getElementById("addEngines");
|
||||
let searchEnginesURL = Services.wm.getMostRecentWindow("navigator:browser")
|
||||
.BrowserSearch.searchEnginesURL;
|
||||
addEnginesLink.setAttribute("href", searchEnginesURL);
|
||||
if (Services.policies &&
|
||||
!Services.policies.isAllowed("installSearchEngine")) {
|
||||
document.getElementById("addEnginesBox").hidden = true;
|
||||
} else {
|
||||
let addEnginesLink = document.getElementById("addEngines");
|
||||
let searchEnginesURL = Services.wm.getMostRecentWindow("navigator:browser")
|
||||
.BrowserSearch.searchEnginesURL;
|
||||
addEnginesLink.setAttribute("href", searchEnginesURL);
|
||||
}
|
||||
|
||||
window.addEventListener("click", this);
|
||||
window.addEventListener("command", this);
|
||||
|
@ -20,8 +20,6 @@
|
||||
title="&languages.customize.Header;"
|
||||
buttons="accept,cancel,help"
|
||||
persist="lastSelected screenX screenY"
|
||||
closebuttonlabel="&preferencesCloseButton.label;"
|
||||
closebuttonaccesskey="&preferencesCloseButton.accesskey;"
|
||||
role="dialog"
|
||||
onload="gLanguagesDialog.init();"
|
||||
helpTopic="prefs-languages"
|
||||
|
@ -21,8 +21,6 @@
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
buttons="accept,cancel,help"
|
||||
persist="lastSelected screenX screenY"
|
||||
closebuttonlabel="&preferencesCloseButton.label;"
|
||||
closebuttonaccesskey="&preferencesCloseButton.accesskey;"
|
||||
role="dialog"
|
||||
ondialoghelp="openPrefsHelp()"
|
||||
style="width: &sanitizePrefs2.modal.width;"
|
||||
|
@ -263,6 +263,7 @@ let gSiteDataSettings = {
|
||||
onClickTreeCol(e) {
|
||||
this._sortSites(this._sites, e.target);
|
||||
this._buildSitesList(this._sites);
|
||||
this._list.clearSelection();
|
||||
},
|
||||
|
||||
onCommandSearch() {
|
||||
|
@ -3426,7 +3426,8 @@ var SessionStoreInternal = {
|
||||
skipAnimation: true,
|
||||
noInitialLabel: true,
|
||||
userContextId,
|
||||
skipBackgroundNotify: true });
|
||||
skipBackgroundNotify: true,
|
||||
bulkOrderedOpen: this._browserSetState });
|
||||
|
||||
if (select) {
|
||||
let leftoverTab = tabbrowser.selectedTab;
|
||||
|
@ -336,6 +336,10 @@ var ContentLinkHandler = {
|
||||
iconAdded = handleFaviconLink(link, isRichIcon, chromeGlobal, faviconLoads);
|
||||
break;
|
||||
case "search":
|
||||
if (Services.policies &&
|
||||
!Services.policies.isAllowed("installSearchEngine")) {
|
||||
break;
|
||||
}
|
||||
if (!searchAdded && event.type == "DOMLinkAdded") {
|
||||
var type = link.type && link.type.toLowerCase();
|
||||
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
|
||||
|
@ -25,4 +25,7 @@ const ThemeVariableMap = [
|
||||
["--lwt-toolbarbutton-hover-background", "button_background_hover"],
|
||||
["--lwt-toolbarbutton-active-background", "button_background_active"],
|
||||
["--lwt-selected-tab-background-color", "tab_selected"],
|
||||
["--lwt-toolbar-field-focus", "toolbar_field_focus"],
|
||||
["--lwt-toolbar-field-focus-color", "toolbar_field_text_focus"],
|
||||
["--lwt-toolbar-field-focus-border-color", "toolbar_field_border_focus"],
|
||||
];
|
||||
|
@ -1011,6 +1011,7 @@ menu.subviewbutton@menuStateHover@,
|
||||
menuitem.subviewbutton@menuStateHover@,
|
||||
.widget-overflow-list .toolbarbutton-1@buttonStateHover@,
|
||||
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
|
||||
color: inherit;
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
@ -1020,6 +1021,7 @@ menu.subviewbutton@menuStateActive@,
|
||||
menuitem.subviewbutton@menuStateActive@,
|
||||
.widget-overflow-list .toolbarbutton-1@buttonStateActive@,
|
||||
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
|
||||
color: inherit;
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
|
||||
}
|
||||
|
@ -59,12 +59,17 @@
|
||||
}
|
||||
|
||||
#urlbar:-moz-lwtheme:hover,
|
||||
#urlbar:-moz-lwtheme[focused="true"],
|
||||
#navigator-toolbox .searchbar-textbox:-moz-lwtheme:hover,
|
||||
#navigator-toolbox .searchbar-textbox:-moz-lwtheme[focused="true"] {
|
||||
#navigator-toolbox .searchbar-textbox:-moz-lwtheme:hover {
|
||||
background-color: var(--url-and-searchbar-background-color, white);
|
||||
}
|
||||
|
||||
#urlbar:-moz-lwtheme[focused="true"],
|
||||
#navigator-toolbox .searchbar-textbox:-moz-lwtheme[focused="true"] {
|
||||
background-color: var(--lwt-toolbar-field-focus, var(--url-and-searchbar-background-color, white));
|
||||
color: var(--lwt-toolbar-field-focus-color, var(--url-and-searchbar-color, black));
|
||||
border-color: var(--lwt-toolbar-field-focus-border-color, var(--lwt-toolbar-field-border-color, @fieldBorderColor@));
|
||||
}
|
||||
|
||||
:root[uidensity=compact] #urlbar,
|
||||
:root[uidensity=compact] .searchbar-textbox {
|
||||
min-height: 26px;
|
||||
|
@ -26,7 +26,7 @@ function PerformanceTelemetry(emitter) {
|
||||
this.onViewSelected = this.onViewSelected.bind(this);
|
||||
|
||||
for (let [event] of EVENT_MAP_FLAGS) {
|
||||
this._emitter.on(event, this.onFlagEvent);
|
||||
this._emitter.on(event, this.onFlagEvent.bind(this, event));
|
||||
}
|
||||
|
||||
this._emitter.on(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
|
||||
@ -55,7 +55,7 @@ PerformanceTelemetry.prototype.onFlagEvent = function(eventName, ...data) {
|
||||
this._telemetry.log(EVENT_MAP_FLAGS.get(eventName), true);
|
||||
};
|
||||
|
||||
PerformanceTelemetry.prototype.onRecordingStateChange = function(_, status, model) {
|
||||
PerformanceTelemetry.prototype.onRecordingStateChange = function(status, model) {
|
||||
if (status != "recording-stopped") {
|
||||
return;
|
||||
}
|
||||
@ -77,7 +77,7 @@ PerformanceTelemetry.prototype.onRecordingStateChange = function(_, status, mode
|
||||
}
|
||||
};
|
||||
|
||||
PerformanceTelemetry.prototype.onViewSelected = function(_, viewName) {
|
||||
PerformanceTelemetry.prototype.onViewSelected = function(viewName) {
|
||||
if (this._previousView) {
|
||||
this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ const MountainGraphWidget = require("devtools/client/shared/widgets/MountainGrap
|
||||
const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
|
||||
|
||||
const defer = require("devtools/shared/defer");
|
||||
const EventEmitter = require("devtools/shared/old-event-emitter");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
const { colorUtils } = require("devtools/shared/css/color");
|
||||
const { getColor } = require("devtools/client/shared/theme");
|
||||
|
@ -7,7 +7,7 @@
|
||||
* This file contains the rendering code for the marker sidebar.
|
||||
*/
|
||||
|
||||
const EventEmitter = require("devtools/shared/old-event-emitter");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { MarkerDOMUtils } = require("devtools/client/performance/modules/marker-dom-utils");
|
||||
|
||||
/**
|
||||
|
@ -7,8 +7,7 @@
|
||||
|
||||
const defer = require("devtools/shared/defer");
|
||||
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/shared/old-event-emitter");
|
||||
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
||||
|
||||
function PerformancePanel(iframeWindow, toolbox) {
|
||||
this.panelWin = iframeWindow;
|
||||
|
@ -44,7 +44,7 @@ var RecordingListItem = React.createFactory(require("devtools/client/performance
|
||||
var Services = require("Services");
|
||||
var promise = require("promise");
|
||||
const defer = require("devtools/shared/defer");
|
||||
var EventEmitter = require("devtools/shared/old-event-emitter");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
var flags = require("devtools/shared/flags");
|
||||
var system = require("devtools/shared/system");
|
||||
@ -127,8 +127,14 @@ var PerformanceController = {
|
||||
this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
this._onThemeChanged = this._onThemeChanged.bind(this);
|
||||
this._onFrontEvent = this._onFrontEvent.bind(this);
|
||||
this._pipe = this._pipe.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
this._onProfilerStatus = this._onProfilerStatus.bind(this);
|
||||
this._onRecordingStarted =
|
||||
this._emitRecordingStateChange.bind(this, "recording-started");
|
||||
this._onRecordingStopping =
|
||||
this._emitRecordingStateChange.bind(this, "recording-stopping");
|
||||
this._onRecordingStopped =
|
||||
this._emitRecordingStateChange.bind(this, "recording-stopped");
|
||||
|
||||
// Store data regarding if e10s is enabled.
|
||||
this._e10s = Services.appinfo.browserTabsRemoteAutostart;
|
||||
@ -145,7 +151,7 @@ var PerformanceController = {
|
||||
PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
||||
RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.on(EVENTS.UI_RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._pipe);
|
||||
DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
|
||||
this._prefObserver = new PrefObserver("devtools.");
|
||||
this._prefObserver.on("devtools.theme", this._onThemeChanged);
|
||||
@ -166,7 +172,7 @@ var PerformanceController = {
|
||||
PerformanceView.off(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
||||
RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.off(EVENTS.UI_RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
DetailsView.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this._pipe);
|
||||
DetailsView.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
|
||||
this._prefObserver.off("devtools.theme", this._onThemeChanged);
|
||||
this._prefObserver.destroy();
|
||||
@ -183,14 +189,20 @@ var PerformanceController = {
|
||||
* listeners are added too soon.
|
||||
*/
|
||||
enableFrontEventListeners: function() {
|
||||
gFront.on("*", this._onFrontEvent);
|
||||
gFront.on("profiler-status", this._onProfilerStatus);
|
||||
gFront.on("recording-started", this._onRecordingStarted);
|
||||
gFront.on("recording-stopping", this._onRecordingStopping);
|
||||
gFront.on("recording-stopped", this._onRecordingStopped);
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables front event listeners.
|
||||
*/
|
||||
disableFrontEventListeners: function() {
|
||||
gFront.off("*", this._onFrontEvent);
|
||||
gFront.off("profiler-status", this._onProfilerStatus);
|
||||
gFront.off("recording-started", this._onRecordingStarted);
|
||||
gFront.off("recording-stopping", this._onRecordingStopping);
|
||||
gFront.off("recording-stopped", this._onRecordingStopped);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -298,7 +310,7 @@ var PerformanceController = {
|
||||
* @param nsIFile file
|
||||
* The file to stream the data into.
|
||||
*/
|
||||
async exportRecording(_, recording, file) {
|
||||
async exportRecording(recording, file) {
|
||||
await recording.exportRecording(file);
|
||||
this.emit(EVENTS.RECORDING_EXPORTED, recording, file);
|
||||
},
|
||||
@ -342,7 +354,7 @@ var PerformanceController = {
|
||||
* @param nsIFile file
|
||||
* The file to import the data from.
|
||||
*/
|
||||
async importRecording(_, file) {
|
||||
async importRecording(file) {
|
||||
let recording = await gFront.importRecording(file);
|
||||
this._addRecordingIfUnknown(recording);
|
||||
|
||||
@ -388,7 +400,7 @@ var PerformanceController = {
|
||||
* Fired from RecordingsView, we listen on the PerformanceController so we can
|
||||
* set it here and re-emit on the controller, where all views can listen.
|
||||
*/
|
||||
_onRecordingSelectFromView: function(_, recording) {
|
||||
_onRecordingSelectFromView: function(recording) {
|
||||
this.setCurrentRecording(recording);
|
||||
},
|
||||
|
||||
@ -396,7 +408,7 @@ var PerformanceController = {
|
||||
* Fired when the ToolbarView fires a PREF_CHANGED event.
|
||||
* with the value.
|
||||
*/
|
||||
_onPrefChanged: function(_, prefName, prefValue) {
|
||||
_onPrefChanged: function(prefName, prefValue) {
|
||||
this.emit(EVENTS.PREF_CHANGED, prefName, prefValue);
|
||||
},
|
||||
|
||||
@ -408,23 +420,13 @@ var PerformanceController = {
|
||||
this.emit(EVENTS.THEME_CHANGED, newValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired from the front on any event. Propagates to other handlers from here.
|
||||
*/
|
||||
_onFrontEvent: function(eventName, ...data) {
|
||||
switch (eventName) {
|
||||
case "profiler-status":
|
||||
let [profilerStatus] = data;
|
||||
this.emit(EVENTS.RECORDING_PROFILER_STATUS_UPDATE, profilerStatus);
|
||||
break;
|
||||
case "recording-started":
|
||||
case "recording-stopping":
|
||||
case "recording-stopped":
|
||||
let [recordingModel] = data;
|
||||
this._addRecordingIfUnknown(recordingModel);
|
||||
this.emit(EVENTS.RECORDING_STATE_CHANGE, eventName, recordingModel);
|
||||
break;
|
||||
}
|
||||
_onProfilerStatus: function(status) {
|
||||
this.emit(EVENTS.RECORDING_PROFILER_STATUS_UPDATE, status);
|
||||
},
|
||||
|
||||
_emitRecordingStateChange(eventName, recordingModel) {
|
||||
this._addRecordingIfUnknown(recordingModel);
|
||||
this.emit(EVENTS.RECORDING_STATE_CHANGE, eventName, recordingModel);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -561,10 +563,10 @@ var PerformanceController = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Pipes an event from some source to the PerformanceController.
|
||||
* Pipes EVENTS.UI_DETAILS_VIEW_SELECTED to the PerformanceController.
|
||||
*/
|
||||
_pipe: function(eventName, ...data) {
|
||||
this.emit(eventName, ...data);
|
||||
_onDetailsViewSelected: function(...data) {
|
||||
this.emit(EVENTS.UI_DETAILS_VIEW_SELECTED, ...data);
|
||||
},
|
||||
|
||||
toString: () => "[object PerformanceController]"
|
||||
|
@ -324,7 +324,7 @@ var PerformanceView = {
|
||||
/**
|
||||
* When starting a recording has failed.
|
||||
*/
|
||||
_onNewRecordingFailed: function(e) {
|
||||
_onNewRecordingFailed: function() {
|
||||
this._lockRecordButtons(false);
|
||||
this._toggleRecordButtons(false);
|
||||
},
|
||||
@ -369,7 +369,7 @@ var PerformanceView = {
|
||||
/**
|
||||
* Fired when a recording is selected. Used to toggle the profiler view state.
|
||||
*/
|
||||
_onRecordingSelected: function(_, recording) {
|
||||
_onRecordingSelected: function(recording) {
|
||||
if (!recording) {
|
||||
this.setState("empty");
|
||||
} else if (recording.isRecording() && recording.isConsole()) {
|
||||
@ -385,7 +385,7 @@ var PerformanceView = {
|
||||
* Fired when the controller has updated information on the buffer's status.
|
||||
* Update the buffer status display if shown.
|
||||
*/
|
||||
_onProfilerStatusUpdated: function(_, profilerStatus) {
|
||||
_onProfilerStatusUpdated: function(profilerStatus) {
|
||||
// We only care about buffer status here, so check to see
|
||||
// if it has position.
|
||||
if (!profilerStatus || profilerStatus.position === void 0) {
|
||||
|
@ -24,12 +24,12 @@ add_task(async function() {
|
||||
|
||||
let uiStartClick = once(PerformanceView, EVENTS.UI_START_RECORDING);
|
||||
let recordingStarted = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-started" }
|
||||
expectedArgs: ["recording-started"]
|
||||
});
|
||||
let backendStartReady = once(PerformanceController,
|
||||
EVENTS.BACKEND_READY_AFTER_RECORDING_START);
|
||||
let uiStateRecording = once(PerformanceView, EVENTS.UI_STATE_CHANGED, {
|
||||
expectedArgs: { "1": "recording" }
|
||||
expectedArgs: ["recording"]
|
||||
});
|
||||
|
||||
click(recordButton);
|
||||
@ -46,12 +46,12 @@ add_task(async function() {
|
||||
|
||||
let uiStopClick = once(PerformanceView, EVENTS.UI_STOP_RECORDING);
|
||||
let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopped" }
|
||||
expectedArgs: ["recording-stopped"]
|
||||
});
|
||||
let backendStopReady = once(PerformanceController,
|
||||
EVENTS.BACKEND_READY_AFTER_RECORDING_STOP);
|
||||
let uiStateRecorded = once(PerformanceView, EVENTS.UI_STATE_CHANGED, {
|
||||
expectedArgs: { "1": "recorded" }
|
||||
expectedArgs: ["recorded"]
|
||||
});
|
||||
|
||||
click(recordButton);
|
||||
|
@ -46,7 +46,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
let stopped = waitForRecordingStoppedEvents(panel, {
|
||||
|
@ -44,7 +44,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
let stopped = waitForRecordingStoppedEvents(panel, {
|
||||
|
@ -44,7 +44,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
let stopped = waitForRecordingStoppedEvents(panel, {
|
||||
|
@ -36,7 +36,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
started = waitForRecordingStartedEvents(panel, {
|
||||
@ -58,7 +58,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
let stopped = waitForRecordingStoppedEvents(panel, {
|
||||
|
@ -166,7 +166,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
info("Ending console.profileEnd()...");
|
||||
@ -193,7 +193,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
info("Recording 5 - Start one more manual recording.");
|
||||
@ -210,7 +210,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview is still rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
info("Recording 5 - Stop manual recording.");
|
||||
|
@ -30,21 +30,21 @@ add_task(async function() {
|
||||
let viewChanged = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED,
|
||||
{ spreadArgs: true });
|
||||
command($("toolbarbutton[data-view='js-calltree']"));
|
||||
let [, viewName] = await viewChanged;
|
||||
let [viewName] = await viewChanged;
|
||||
is(viewName, "js-calltree", "UI_DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, $, "js-calltree");
|
||||
|
||||
// Select js flamegraph view.
|
||||
viewChanged = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED, { spreadArgs: true });
|
||||
command($("toolbarbutton[data-view='js-flamegraph']"));
|
||||
[, viewName] = await viewChanged;
|
||||
[viewName] = await viewChanged;
|
||||
is(viewName, "js-flamegraph", "UI_DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, $, "js-flamegraph");
|
||||
|
||||
// Select waterfall view.
|
||||
viewChanged = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED, { spreadArgs: true });
|
||||
command($("toolbarbutton[data-view='waterfall']"));
|
||||
[, viewName] = await viewChanged;
|
||||
[viewName] = await viewChanged;
|
||||
is(viewName, "waterfall", "UI_DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, $, "waterfall");
|
||||
|
||||
|
@ -32,8 +32,9 @@ add_task(async function() {
|
||||
ok(DetailsView.isViewSelected(JsCallTreeView),
|
||||
"The js calltree view is now selected in the details view.");
|
||||
|
||||
let cleared = once(PerformanceController, EVENTS.RECORDING_SELECTED,
|
||||
{ expectedArgs: { "1": null } });
|
||||
let cleared = once(PerformanceController, EVENTS.RECORDING_SELECTED, {
|
||||
expectedArgs: [null]
|
||||
});
|
||||
await PerformanceController.clearRecordings();
|
||||
await cleared;
|
||||
|
||||
|
@ -28,10 +28,10 @@ add_task(async function() {
|
||||
"The duration node should show the 'recording' message while recording");
|
||||
|
||||
let recordingStopping = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopping" }
|
||||
expectedArgs: ["recording-stopping"]
|
||||
});
|
||||
let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopped" }
|
||||
expectedArgs: ["recording-stopped"]
|
||||
});
|
||||
let everythingStopped = stopRecording(panel);
|
||||
|
||||
|
@ -33,10 +33,10 @@ add_task(async function() {
|
||||
"The recording-notice is shown while recording.");
|
||||
|
||||
let recordingStopping = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopping" }
|
||||
expectedArgs: ["recording-stopping"]
|
||||
});
|
||||
let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopped" }
|
||||
expectedArgs: ["recording-stopped"]
|
||||
});
|
||||
let everythingStopped = stopRecording(panel);
|
||||
|
||||
@ -57,10 +57,10 @@ add_task(async function() {
|
||||
await recordingSelected;
|
||||
|
||||
recordingStopping = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopping" }
|
||||
expectedArgs: ["recording-stopping"]
|
||||
});
|
||||
recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopped" }
|
||||
expectedArgs: ["recording-stopped"]
|
||||
});
|
||||
everythingStopped = stopRecording(panel);
|
||||
|
||||
|
@ -24,7 +24,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview keeps rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
ok(true, "Overview was rendered while recording.");
|
||||
|
@ -54,7 +54,7 @@ add_task(async function() {
|
||||
|
||||
// Ensure overview keeps rendering.
|
||||
await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
});
|
||||
|
||||
ok("selectionEnabled" in framerate,
|
||||
|
@ -31,13 +31,13 @@ add_task(async function() {
|
||||
let rangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED,
|
||||
{ spreadArgs: true });
|
||||
dragStartCanvasGraph(graph, { x: 0 });
|
||||
let [, { startTime, endTime }] = await rangeSelected;
|
||||
let [{ startTime, endTime }] = await rangeSelected;
|
||||
is(endTime, duration, "The selected range is the entire graph, for now.");
|
||||
|
||||
rangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED,
|
||||
{ spreadArgs: true });
|
||||
dragStopCanvasGraph(graph, { x: graph.width / 2 });
|
||||
[, { startTime, endTime }] = await rangeSelected;
|
||||
[{ startTime, endTime }] = await rangeSelected;
|
||||
is(endTime, duration / 2, "The selected range is half of the graph.");
|
||||
|
||||
is(graph.hasSelection(), true,
|
||||
@ -58,7 +58,7 @@ add_task(async function() {
|
||||
rangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED,
|
||||
{ spreadArgs: true });
|
||||
clickCanvasGraph(graph, { x: 3 * graph.width / 4 });
|
||||
[, { startTime, endTime }] = await rangeSelected;
|
||||
[{ startTime, endTime }] = await rangeSelected;
|
||||
|
||||
is(graph.hasSelection(), false,
|
||||
"A selection no longer on the graph.");
|
||||
|
@ -52,8 +52,7 @@ add_task(async function() {
|
||||
await startRecording(panel);
|
||||
|
||||
await waitUntil(async function() {
|
||||
[, gPercent] = await once(PerformanceView,
|
||||
EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
[gPercent] = await once(PerformanceView, EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
{ spreadArgs: true });
|
||||
return gPercent > 0;
|
||||
});
|
||||
@ -71,8 +70,7 @@ add_task(async function() {
|
||||
await console.profile("rust");
|
||||
|
||||
await waitUntil(async function() {
|
||||
[, gPercent] = await once(PerformanceView,
|
||||
EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
[gPercent] = await once(PerformanceView, EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
{ spreadArgs: true });
|
||||
return gPercent > Math.floor(bufferUsage * 100);
|
||||
});
|
||||
@ -92,8 +90,7 @@ add_task(async function() {
|
||||
await selected;
|
||||
|
||||
await waitUntil(async function() {
|
||||
[, gPercent] = await once(PerformanceView,
|
||||
EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
[gPercent] = await once(PerformanceView, EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
{ spreadArgs: true });
|
||||
return gPercent > 0;
|
||||
});
|
||||
@ -113,7 +110,7 @@ add_task(async function() {
|
||||
await selected;
|
||||
|
||||
await waitUntil(async function() {
|
||||
[, gPercent] = await once(PerformanceView,
|
||||
[gPercent] = await once(PerformanceView,
|
||||
EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
{ spreadArgs: true });
|
||||
return gPercent > Math.floor(bufferUsage * 100);
|
||||
|
@ -41,8 +41,7 @@ add_task(async function() {
|
||||
await startRecording(panel);
|
||||
|
||||
await waitUntil(async function() {
|
||||
[, gPercent] = await once(PerformanceView,
|
||||
EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
[gPercent] = await once(PerformanceView, EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
|
||||
{ spreadArgs: true });
|
||||
return gPercent == 100;
|
||||
});
|
||||
|
@ -43,7 +43,7 @@ add_task(async function() {
|
||||
|
||||
let recordingDeleted = times(PerformanceController, EVENTS.RECORDING_DELETED, 2);
|
||||
let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopped" }
|
||||
expectedArgs: ["recording-stopped"]
|
||||
});
|
||||
|
||||
PerformanceController.clearRecordings();
|
||||
|
@ -32,14 +32,14 @@ add_task(async function() {
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
||||
|
||||
let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
|
||||
await PerformanceController.exportRecording("",
|
||||
PerformanceController.getCurrentRecording(), file);
|
||||
await PerformanceController.exportRecording(PerformanceController.getCurrentRecording(),
|
||||
file);
|
||||
await exported;
|
||||
|
||||
ok(logs[EXPORTED], `A telemetry entry for ${EXPORTED} exists after exporting.`);
|
||||
|
||||
let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
|
||||
await PerformanceController.importRecording(null, file);
|
||||
await PerformanceController.importRecording(file);
|
||||
await imported;
|
||||
|
||||
ok(logs[IMPORTED], `A telemetry entry for ${IMPORTED} exists after importing.`);
|
||||
|
@ -50,17 +50,17 @@ exports.waitForRecordingStartedEvents = function(panel, options = {}) {
|
||||
options.skipWaitingForRecordingStarted
|
||||
? null
|
||||
: once(controller, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-started" }
|
||||
expectedArgs: ["recording-started"]
|
||||
}),
|
||||
options.skipWaitingForViewState
|
||||
? null
|
||||
: once(view, EVENTS.UI_STATE_CHANGED, {
|
||||
expectedArgs: { "1": options.expectedViewState }
|
||||
expectedArgs: [options.expectedViewState]
|
||||
}),
|
||||
options.skipWaitingForOverview
|
||||
? null
|
||||
: once(overview, EVENTS.UI_OVERVIEW_RENDERED, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
}),
|
||||
]);
|
||||
};
|
||||
@ -86,22 +86,22 @@ exports.waitForRecordingStoppedEvents = function(panel, options = {}) {
|
||||
options.skipWaitingForRecordingStop
|
||||
? null
|
||||
: once(controller, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopping" }
|
||||
expectedArgs: ["recording-stopping"]
|
||||
}),
|
||||
options.skipWaitingForRecordingStop
|
||||
? null
|
||||
: once(controller, EVENTS.RECORDING_STATE_CHANGE, {
|
||||
expectedArgs: { "1": "recording-stopped" }
|
||||
expectedArgs: ["recording-stopped"]
|
||||
}),
|
||||
options.skipWaitingForViewState
|
||||
? null
|
||||
: once(view, EVENTS.UI_STATE_CHANGED, {
|
||||
expectedArgs: { "1": options.expectedViewState }
|
||||
expectedArgs: [options.expectedViewState]
|
||||
}),
|
||||
options.skipWaitingForOverview
|
||||
? null
|
||||
: once(overview, EVENTS.UI_OVERVIEW_RENDERED, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_HIGH_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_HIGH_RES_INTERVAL]
|
||||
}),
|
||||
options.skipWaitingForSubview
|
||||
? null
|
||||
@ -138,7 +138,7 @@ exports.waitForOverviewRenderedWithMarkers = (panel, minTimes = 3, minMarkers =
|
||||
|
||||
return Promise.all([
|
||||
times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, minTimes, {
|
||||
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
|
||||
expectedArgs: [Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL]
|
||||
}),
|
||||
waitUntil(() =>
|
||||
PerformanceController.getCurrentRecording().getMarkers().length >= minMarkers
|
||||
|
@ -49,17 +49,14 @@ exports.times = function(target, eventName, receiveCount, options = {}) {
|
||||
|
||||
target[add](eventName, function onEvent(...args) {
|
||||
if ("expectedArgs" in options) {
|
||||
for (let index of Object.keys(options.expectedArgs)) {
|
||||
for (let [index, expectedValue] of options.expectedArgs.entries()) {
|
||||
const isExpectedValueRegExp = expectedValue instanceof RegExp;
|
||||
if (
|
||||
// Expected argument matches this regexp.
|
||||
(options.expectedArgs[index] instanceof RegExp &&
|
||||
!options.expectedArgs[index].exec(args[index])) ||
|
||||
// Expected argument is not a regexp and equal to the received arg.
|
||||
(!(options.expectedArgs[index] instanceof RegExp) &&
|
||||
options.expectedArgs[index] != args[index])
|
||||
(isExpectedValueRegExp && !expectedValue.exec(args[index])) ||
|
||||
(!isExpectedValueRegExp && expectedValue != args[index])
|
||||
) {
|
||||
dump(`Ignoring event '${eventName}' with unexpected argument at index ` +
|
||||
`${index}: ${args[index]}\n`);
|
||||
`${index}: ${args[index]} - expected ${expectedValue}\n`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ var DetailsSubview = {
|
||||
/**
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function(_, state, recording) {
|
||||
_onRecordingStoppedOrSelected: function(state, recording) {
|
||||
if (typeof state !== "string") {
|
||||
recording = state;
|
||||
}
|
||||
@ -132,7 +132,7 @@ var DetailsSubview = {
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onOverviewRangeChange: function(_, interval) {
|
||||
_onOverviewRangeChange: function(interval) {
|
||||
if (!this.requiresUpdateOnRangeChange) {
|
||||
return;
|
||||
}
|
||||
@ -165,7 +165,7 @@ var DetailsSubview = {
|
||||
/**
|
||||
* Fired when a preference in `devtools.performance.ui.` is changed.
|
||||
*/
|
||||
_onPrefChanged: function(_, prefName, prefValue) {
|
||||
_onPrefChanged: function(prefName, prefValue) {
|
||||
if (~this.observedPrefs.indexOf(prefName) && this._onObservedPrefChange) {
|
||||
this._onObservedPrefChange(prefName);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ var JsCallTreeView = extend(DetailsSubview, {
|
||||
/**
|
||||
* Fired on the "link" event for the call tree in this container.
|
||||
*/
|
||||
_onLink: function(_, treeItem) {
|
||||
_onLink: function(treeItem) {
|
||||
let { url, line } = treeItem.frame.getInfo();
|
||||
gToolbox.viewSourceInDebugger(url, line).then(success => {
|
||||
if (success) {
|
||||
|
@ -114,7 +114,7 @@ var JsFlameGraphView = extend(DetailsSubview, {
|
||||
/**
|
||||
* Called when `devtools.theme` changes.
|
||||
*/
|
||||
_onThemeChanged: function(_, theme) {
|
||||
_onThemeChanged: function(theme) {
|
||||
this.graph.setTheme(theme);
|
||||
this.graph.refresh({ force: true });
|
||||
},
|
||||
|
@ -56,7 +56,7 @@ var MemoryCallTreeView = extend(DetailsSubview, {
|
||||
/**
|
||||
* Fired on the "link" event for the call tree in this container.
|
||||
*/
|
||||
_onLink: function(_, treeItem) {
|
||||
_onLink: function(treeItem) {
|
||||
let { url, line } = treeItem.frame.getInfo();
|
||||
gToolbox.viewSourceInDebugger(url, line).then(success => {
|
||||
if (success) {
|
||||
|
@ -111,7 +111,7 @@ var MemoryFlameGraphView = extend(DetailsSubview, {
|
||||
/**
|
||||
* Called when `devtools.theme` changes.
|
||||
*/
|
||||
_onThemeChanged: function(_, theme) {
|
||||
_onThemeChanged: function(theme) {
|
||||
this.graph.setTheme(theme);
|
||||
this.graph.refresh({ force: true });
|
||||
},
|
||||
|
@ -145,14 +145,14 @@ var WaterfallView = extend(DetailsSubview, {
|
||||
/**
|
||||
* Called when MarkerDetails view emits an event to view source.
|
||||
*/
|
||||
_onViewSource: function(_, data) {
|
||||
_onViewSource: function(data) {
|
||||
gToolbox.viewSourceInDebugger(data.url, data.line);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when MarkerDetails view emits an event to snap to allocations.
|
||||
*/
|
||||
_onShowAllocations: function(_, data) {
|
||||
_onShowAllocations: function(data) {
|
||||
let { endTime } = data;
|
||||
let startTime = 0;
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
|
@ -240,7 +240,7 @@ var DetailsView = {
|
||||
/**
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function(_, state, recording) {
|
||||
_onRecordingStoppedOrSelected: function(state, recording) {
|
||||
if (typeof state === "string" && state !== "recording-stopped") {
|
||||
return;
|
||||
}
|
||||
|
@ -209,27 +209,26 @@ var OverviewView = {
|
||||
/**
|
||||
* Called when recording state changes.
|
||||
*/
|
||||
_onRecordingStateChange:
|
||||
OverviewViewOnStateChange(async function(_, state, recording) {
|
||||
if (state !== "recording-stopped") {
|
||||
return;
|
||||
}
|
||||
// Check to see if the recording that just stopped is the current recording.
|
||||
// If it is, render the high-res graphs. For manual recordings, it will also
|
||||
// be the current recording, but profiles generated by `console.profile` can stop
|
||||
// while having another profile selected -- in this case, OverviewView should keep
|
||||
// rendering the current recording.
|
||||
if (recording !== PerformanceController.getCurrentRecording()) {
|
||||
return;
|
||||
}
|
||||
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||
await this._checkSelection(recording);
|
||||
}),
|
||||
_onRecordingStateChange: OverviewViewOnStateChange(async function(state, recording) {
|
||||
if (state !== "recording-stopped") {
|
||||
return;
|
||||
}
|
||||
// Check to see if the recording that just stopped is the current recording.
|
||||
// If it is, render the high-res graphs. For manual recordings, it will also
|
||||
// be the current recording, but profiles generated by `console.profile` can stop
|
||||
// while having another profile selected -- in this case, OverviewView should keep
|
||||
// rendering the current recording.
|
||||
if (recording !== PerformanceController.getCurrentRecording()) {
|
||||
return;
|
||||
}
|
||||
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||
await this._checkSelection(recording);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Called when a new recording is selected.
|
||||
*/
|
||||
_onRecordingSelected: OverviewViewOnStateChange(async function(_, recording) {
|
||||
_onRecordingSelected: OverviewViewOnStateChange(async function(recording) {
|
||||
this._setGraphVisibilityFromRecordingFeatures(recording);
|
||||
|
||||
// If this recording is complete, render the high res graph
|
||||
@ -283,7 +282,7 @@ var OverviewView = {
|
||||
this.emit(EVENTS.UI_OVERVIEW_RANGE_SELECTED, this.getTimeInterval());
|
||||
},
|
||||
|
||||
_onGraphRendered: function(_, graphName) {
|
||||
_onGraphRendered: function(graphName) {
|
||||
switch (graphName) {
|
||||
case "timeline":
|
||||
this.emit(EVENTS.UI_MARKERS_GRAPH_RENDERED);
|
||||
@ -303,7 +302,7 @@ var OverviewView = {
|
||||
* because those will set values on a recording model, and
|
||||
* the graphs will render based on the existence.
|
||||
*/
|
||||
async _onPrefChanged(_, prefName, prefValue) {
|
||||
async _onPrefChanged(prefName, prefValue) {
|
||||
switch (prefName) {
|
||||
case "hidden-markers": {
|
||||
let graph = await this.graphs.isAvailable("timeline");
|
||||
@ -356,7 +355,7 @@ var OverviewView = {
|
||||
/**
|
||||
* Called when `devtools.theme` changes.
|
||||
*/
|
||||
_onThemeChanged: function(_, theme) {
|
||||
_onThemeChanged: function(theme) {
|
||||
this.graphs.setTheme({ theme, redraw: true });
|
||||
},
|
||||
|
||||
@ -374,11 +373,11 @@ var OverviewView = {
|
||||
* @return {function}
|
||||
*/
|
||||
function OverviewViewOnStateChange(fn) {
|
||||
return function _onRecordingStateChange(eventName, recording) {
|
||||
return function _onRecordingStateChange(recording) {
|
||||
// Normalize arguments for the RECORDING_STATE_CHANGE event,
|
||||
// as it also has a `state` argument.
|
||||
if (typeof recording === "string") {
|
||||
recording = arguments[2];
|
||||
recording = arguments[1];
|
||||
}
|
||||
|
||||
let currentRecording = PerformanceController.getCurrentRecording();
|
||||
|
@ -109,8 +109,8 @@ var RecordingsView = {
|
||||
* before the tool is loaded) or imported. In normal manual recording cases,
|
||||
* this will also be fired.
|
||||
*/
|
||||
_onNewRecording: function(_, recording) {
|
||||
this._onRecordingStateChange(_, null, recording);
|
||||
_onNewRecording: function(recording) {
|
||||
this._onRecordingStateChange(null, recording);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -121,7 +121,7 @@ var RecordingsView = {
|
||||
* @param RecordingModel recording
|
||||
* Model of the recording that was started.
|
||||
*/
|
||||
_onRecordingStateChange: function(_, state, recording) {
|
||||
_onRecordingStateChange: function(state, recording) {
|
||||
const { recordings, labels } = this._listState;
|
||||
|
||||
if (!recordings.includes(recording)) {
|
||||
@ -148,7 +148,7 @@ var RecordingsView = {
|
||||
/**
|
||||
* Clears out all non-console recordings.
|
||||
*/
|
||||
_onRecordingDeleted: function(_, recording) {
|
||||
_onRecordingDeleted: function(recording) {
|
||||
const { recordings } = this._listState;
|
||||
const index = recordings.indexOf(recording);
|
||||
if (index === -1) {
|
||||
@ -186,7 +186,7 @@ var RecordingsView = {
|
||||
}});
|
||||
},
|
||||
|
||||
_onRecordingExported: function(_, recording, file) {
|
||||
_onRecordingExported: function(recording, file) {
|
||||
if (recording.isConsole()) {
|
||||
return;
|
||||
}
|
||||
|
@ -64,11 +64,8 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
|
||||
[browser_layout_simple.js]
|
||||
[browser_markers-cycle-collection.js]
|
||||
[browser_markers-docloading-01.js]
|
||||
uses-unsafe-cpows = true
|
||||
[browser_markers-docloading-02.js]
|
||||
uses-unsafe-cpows = true
|
||||
[browser_markers-docloading-03.js]
|
||||
uses-unsafe-cpows = true
|
||||
[browser_markers-gc.js]
|
||||
[browser_markers-minor-gc.js]
|
||||
[browser_markers-parse-html.js]
|
||||
|
@ -11,7 +11,6 @@ const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
|
||||
|
||||
add_task(async function() {
|
||||
let browser = await addTab(MAIN_DOMAIN + "doc_innerHTML.html");
|
||||
let doc = browser.contentDocumentAsCPOW;
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
@ -23,7 +22,9 @@ add_task(async function() {
|
||||
ok(false, "Should not be emitting doc-loading events.");
|
||||
});
|
||||
|
||||
executeSoon(() => doc.location.reload());
|
||||
ContentTask.spawn(browser, null, function() {
|
||||
content.location.reload();
|
||||
});
|
||||
|
||||
await waitForMarkerType(front, MARKER_NAMES, () => true, e => e, "markers");
|
||||
await front.stop(rec);
|
||||
|
@ -11,7 +11,6 @@ const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
|
||||
|
||||
add_task(async function() {
|
||||
let browser = await addTab(MAIN_DOMAIN + "doc_innerHTML.html");
|
||||
let doc = browser.contentDocumentAsCPOW;
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
@ -21,7 +20,9 @@ add_task(async function() {
|
||||
|
||||
await new Promise(resolve => {
|
||||
front.once("doc-loading", resolve);
|
||||
doc.location.reload();
|
||||
ContentTask.spawn(browser, null, function() {
|
||||
content.location.reload();
|
||||
});
|
||||
});
|
||||
|
||||
ok(true, "At least one doc-loading event got fired.");
|
||||
|
@ -11,7 +11,6 @@ const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
|
||||
|
||||
add_task(async function() {
|
||||
let browser = await addTab(MAIN_DOMAIN + "doc_innerHTML.html");
|
||||
let doc = browser.contentDocumentAsCPOW;
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
@ -25,7 +24,9 @@ add_task(async function() {
|
||||
|
||||
await new Promise(resolve => {
|
||||
front.once("doc-loading", resolve);
|
||||
doc.location.reload();
|
||||
ContentTask.spawn(browser, null, function() {
|
||||
content.location.reload();
|
||||
});
|
||||
});
|
||||
|
||||
ok(true, "At least one doc-loading event got fired.");
|
||||
|
@ -1631,137 +1631,6 @@ nsContentUtils::GetBidiKeyboard()
|
||||
return sBidiKeyboard;
|
||||
}
|
||||
|
||||
template <class OutputIterator>
|
||||
struct NormalizeNewlinesCharTraits {
|
||||
public:
|
||||
typedef typename OutputIterator::value_type value_type;
|
||||
|
||||
public:
|
||||
explicit NormalizeNewlinesCharTraits(OutputIterator& aIterator) : mIterator(aIterator) { }
|
||||
void writechar(typename OutputIterator::value_type aChar) {
|
||||
*mIterator++ = aChar;
|
||||
}
|
||||
|
||||
private:
|
||||
OutputIterator mIterator;
|
||||
};
|
||||
|
||||
template <class CharT>
|
||||
struct NormalizeNewlinesCharTraits<CharT*> {
|
||||
public:
|
||||
typedef CharT value_type;
|
||||
|
||||
public:
|
||||
explicit NormalizeNewlinesCharTraits(CharT* aCharPtr) : mCharPtr(aCharPtr) { }
|
||||
void writechar(CharT aChar) {
|
||||
*mCharPtr++ = aChar;
|
||||
}
|
||||
|
||||
private:
|
||||
CharT* mCharPtr;
|
||||
};
|
||||
|
||||
template <class OutputIterator>
|
||||
class CopyNormalizeNewlines
|
||||
{
|
||||
public:
|
||||
typedef typename OutputIterator::value_type value_type;
|
||||
|
||||
public:
|
||||
explicit CopyNormalizeNewlines(OutputIterator* aDestination,
|
||||
bool aLastCharCR = false) :
|
||||
mLastCharCR(aLastCharCR),
|
||||
mDestination(aDestination),
|
||||
mWritten(0)
|
||||
{ }
|
||||
|
||||
uint32_t GetCharsWritten() {
|
||||
return mWritten;
|
||||
}
|
||||
|
||||
bool IsLastCharCR() {
|
||||
return mLastCharCR;
|
||||
}
|
||||
|
||||
void write(const typename OutputIterator::value_type* aSource, uint32_t aSourceLength) {
|
||||
|
||||
const typename OutputIterator::value_type* done_writing = aSource + aSourceLength;
|
||||
|
||||
// If the last source buffer ended with a CR...
|
||||
if (mLastCharCR) {
|
||||
// ..and if the next one is a LF, then skip it since
|
||||
// we've already written out a newline
|
||||
if (aSourceLength && (*aSource == value_type('\n'))) {
|
||||
++aSource;
|
||||
}
|
||||
mLastCharCR = false;
|
||||
}
|
||||
|
||||
uint32_t num_written = 0;
|
||||
while ( aSource < done_writing ) {
|
||||
if (*aSource == value_type('\r')) {
|
||||
mDestination->writechar('\n');
|
||||
++aSource;
|
||||
// If we've reached the end of the buffer, record
|
||||
// that we wrote out a CR
|
||||
if (aSource == done_writing) {
|
||||
mLastCharCR = true;
|
||||
}
|
||||
// If the next character is a LF, skip it
|
||||
else if (*aSource == value_type('\n')) {
|
||||
++aSource;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mDestination->writechar(*aSource++);
|
||||
}
|
||||
++num_written;
|
||||
}
|
||||
|
||||
mWritten += num_written;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mLastCharCR;
|
||||
OutputIterator* mDestination;
|
||||
uint32_t mWritten;
|
||||
};
|
||||
|
||||
// static
|
||||
uint32_t
|
||||
nsContentUtils::CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
|
||||
uint32_t aSrcOffset,
|
||||
char16_t* aDest,
|
||||
uint32_t aLength,
|
||||
bool& aLastCharCR)
|
||||
{
|
||||
typedef NormalizeNewlinesCharTraits<char16_t*> sink_traits;
|
||||
|
||||
sink_traits dest_traits(aDest);
|
||||
CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits,aLastCharCR);
|
||||
nsReadingIterator<char16_t> fromBegin, fromEnd;
|
||||
copy_string(aSource.BeginReading(fromBegin).advance( int32_t(aSrcOffset) ),
|
||||
aSource.BeginReading(fromEnd).advance( int32_t(aSrcOffset+aLength) ),
|
||||
normalizer);
|
||||
aLastCharCR = normalizer.IsLastCharCR();
|
||||
return normalizer.GetCharsWritten();
|
||||
}
|
||||
|
||||
// static
|
||||
uint32_t
|
||||
nsContentUtils::CopyNewlineNormalizedUnicodeTo(nsReadingIterator<char16_t>& aSrcStart, const nsReadingIterator<char16_t>& aSrcEnd, nsAString& aDest)
|
||||
{
|
||||
typedef nsWritingIterator<char16_t> WritingIterator;
|
||||
typedef NormalizeNewlinesCharTraits<WritingIterator> sink_traits;
|
||||
|
||||
WritingIterator iter;
|
||||
aDest.BeginWriting(iter);
|
||||
sink_traits dest_traits(iter);
|
||||
CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits);
|
||||
copy_string(aSrcStart, aSrcEnd, normalizer);
|
||||
return normalizer.GetCharsWritten();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to determine whether a character is in one of the classes
|
||||
* which CSS says should be part of the first-letter. Currently, that is
|
||||
|
@ -509,14 +509,6 @@ public:
|
||||
static nsIDocument* GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
|
||||
uint64_t aOuterWindowId);
|
||||
|
||||
static uint32_t CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
|
||||
uint32_t aSrcOffset,
|
||||
char16_t* aDest,
|
||||
uint32_t aLength,
|
||||
bool& aLastCharCR);
|
||||
|
||||
static uint32_t CopyNewlineNormalizedUnicodeTo(nsReadingIterator<char16_t>& aSrcStart, const nsReadingIterator<char16_t>& aSrcEnd, nsAString& aDest);
|
||||
|
||||
static const nsDependentSubstring TrimCharsInSet(const char* aSet,
|
||||
const nsAString& aValue);
|
||||
|
||||
|
@ -4979,7 +4979,7 @@ nsGlobalWindowOuter::HomeOuter(nsIPrincipal& aSubjectPrincipal, ErrorResult& aEr
|
||||
#ifdef DEBUG_seth
|
||||
printf("all else failed. using %s as the home page\n", DEFAULT_HOME_PAGE);
|
||||
#endif
|
||||
CopyASCIItoUTF16(DEFAULT_HOME_PAGE, homeURL);
|
||||
homeURL = NS_LITERAL_STRING(DEFAULT_HOME_PAGE);
|
||||
}
|
||||
|
||||
#ifdef MOZ_PHOENIX
|
||||
|
@ -240,7 +240,6 @@ LOCAL_INCLUDES += [
|
||||
'/dom/security',
|
||||
'/dom/xbl',
|
||||
'/dom/xul',
|
||||
'/editor/txmgr',
|
||||
'/image',
|
||||
'/layout/forms',
|
||||
'/layout/generic',
|
||||
|
@ -172,16 +172,23 @@ public:
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
MOZ_ASSERT(mTextEditor);
|
||||
|
||||
bool canUndo;
|
||||
DebugOnly<nsresult> rv = mTextEditor->CanUndo(&mPreviousEnabled, &canUndo);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
mTextEditor->EnableUndo(false);
|
||||
mPreviousEnabled = mTextEditor->IsUndoRedoEnabled();
|
||||
DebugOnly<bool> disabledUndoRedo = mTextEditor->DisableUndoRedo();
|
||||
NS_WARNING_ASSERTION(disabledUndoRedo,
|
||||
"Failed to disable undo/redo transactions");
|
||||
}
|
||||
|
||||
~AutoDisableUndo()
|
||||
{
|
||||
mTextEditor->EnableUndo(mPreviousEnabled);
|
||||
if (mPreviousEnabled) {
|
||||
DebugOnly<bool> enabledUndoRedo = mTextEditor->EnableUndoRedo();
|
||||
NS_WARNING_ASSERTION(enabledUndoRedo,
|
||||
"Failed to enable undo/redo transactions");
|
||||
} else {
|
||||
DebugOnly<bool> disabledUndoRedo = mTextEditor->DisableUndoRedo();
|
||||
NS_WARNING_ASSERTION(disabledUndoRedo,
|
||||
"Failed to disable undo/redo transactions");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -987,8 +994,8 @@ TextInputListener::OnEditActionHandled()
|
||||
RefPtr<TextEditor> textEditor = frame->GetTextEditor();
|
||||
|
||||
// Get the number of undo / redo items
|
||||
int32_t numUndoItems = textEditor->NumberOfUndoItems();
|
||||
int32_t numRedoItems = textEditor->NumberOfRedoItems();
|
||||
size_t numUndoItems = textEditor->NumberOfUndoItems();
|
||||
size_t numRedoItems = textEditor->NumberOfRedoItems();
|
||||
if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
|
||||
(numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
|
||||
// Modify the menu if undo or redo items are different
|
||||
@ -1508,22 +1515,20 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransactionManager> transactionManager =
|
||||
newTextEditor->GetTransactionManager();
|
||||
if (NS_WARN_IF(!transactionManager)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
transactionManager->SetMaxTransactionCount(
|
||||
nsITextControlElement::DEFAULT_UNDO_CAP);
|
||||
|
||||
if (IsPasswordTextControl()) {
|
||||
// Disable undo for password textfields. Note that we want to do this at
|
||||
// the very end of InitEditor, so the calls to EnableUndo when setting the
|
||||
// default value don't screw us up.
|
||||
// Since changing the control type does a reframe, we don't have to worry
|
||||
// about dynamic type changes here.
|
||||
newTextEditor->EnableUndo(false);
|
||||
// Disable undo for <input type="password">. Note that we want to do this
|
||||
// at the very end of InitEditor(), so the calls to EnableUndoRedo() when
|
||||
// setting the default value don't screw us up. Since changing the
|
||||
// control type does a reframe, we don't have to worry about dynamic type
|
||||
// changes here.
|
||||
DebugOnly<bool> disabledUndoRedo = newTextEditor->DisableUndoRedo();
|
||||
NS_WARNING_ASSERTION(disabledUndoRedo,
|
||||
"Failed to disable undo/redo transaction");
|
||||
} else {
|
||||
DebugOnly<bool> enabledUndoRedo =
|
||||
newTextEditor->EnableUndoRedo(nsITextControlElement::DEFAULT_UNDO_CAP);
|
||||
NS_WARNING_ASSERTION(enabledUndoRedo,
|
||||
"Failed to enable undo/redo transaction");
|
||||
}
|
||||
|
||||
if (!mEditorInitialized) {
|
||||
|
@ -47,7 +47,10 @@ var gTest = {
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Comparing different AudioContexts may result in different timing reated information being reported
|
||||
// when we jitter time, as they are on different Relative Timelines.
|
||||
SpecialPowers.pushPrefEnv({"set": [["privacy.resistFingerprinting.reduceTimerPrecision.jitter", false]]}, runTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIIncrementalStreamLoader.h"
|
||||
#include "nsIUnicharStreamLoader.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "mozilla/ComposerCommandsUpdater.h"
|
||||
|
||||
#include "mozilla/mozalloc.h" // for operator new
|
||||
#include "mozilla/TransactionManager.h" // for TransactionManager
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "nsAString.h"
|
||||
#include "nsComponentManagerUtils.h" // for do_CreateInstance
|
||||
@ -116,8 +117,7 @@ ComposerCommandsUpdater::DidDo(nsITransactionManager* aManager,
|
||||
nsresult aDoResult)
|
||||
{
|
||||
// only need to update if the status of the Undo menu item changes.
|
||||
int32_t undoCount;
|
||||
aManager->GetNumberOfUndoItems(&undoCount);
|
||||
size_t undoCount = aManager->AsTransactionManager()->NumberOfUndoItems();
|
||||
if (undoCount == 1) {
|
||||
if (mFirstDoOfFirstUndo) {
|
||||
UpdateCommandGroup(NS_LITERAL_STRING("undo"));
|
||||
@ -142,11 +142,10 @@ ComposerCommandsUpdater::DidUndo(nsITransactionManager* aManager,
|
||||
nsITransaction* aTransaction,
|
||||
nsresult aUndoResult)
|
||||
{
|
||||
int32_t undoCount;
|
||||
aManager->GetNumberOfUndoItems(&undoCount);
|
||||
if (undoCount == 0)
|
||||
size_t undoCount = aManager->AsTransactionManager()->NumberOfUndoItems();
|
||||
if (!undoCount) {
|
||||
mFirstDoOfFirstUndo = true; // reset the state for the next do
|
||||
|
||||
}
|
||||
UpdateCommandGroup(NS_LITERAL_STRING("undo"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -470,11 +470,11 @@ nsEditingSession::SetupEditorOnWindow(mozIDOMWindowProxy* aWindow)
|
||||
htmlEditor->SetComposerCommandsUpdater(mComposerCommandsUpdater);
|
||||
|
||||
// and as a transaction listener
|
||||
nsCOMPtr<nsITransactionManager> txnMgr;
|
||||
htmlEditor->GetTransactionManager(getter_AddRefs(txnMgr));
|
||||
if (txnMgr) {
|
||||
txnMgr->AddListener(mComposerCommandsUpdater);
|
||||
}
|
||||
MOZ_ASSERT(mComposerCommandsUpdater);
|
||||
DebugOnly<bool> addedTransactionListener =
|
||||
htmlEditor->AddTransactionListener(*mComposerCommandsUpdater);
|
||||
NS_WARNING_ASSERTION(addedTransactionListener,
|
||||
"Failed to add transaction listener to the editor");
|
||||
|
||||
// Set context on all controllers to be the editor
|
||||
rv = SetEditorOnControllers(aWindow, htmlEditor);
|
||||
@ -498,14 +498,11 @@ nsEditingSession::RemoveListenersAndControllers(nsPIDOMWindowOuter* aWindow,
|
||||
|
||||
// Remove all the listeners
|
||||
aHTMLEditor->SetComposerCommandsUpdater(nullptr);
|
||||
|
||||
aHTMLEditor->RemoveDocumentStateListener(mComposerCommandsUpdater);
|
||||
|
||||
nsCOMPtr<nsITransactionManager> txnMgr;
|
||||
aHTMLEditor->GetTransactionManager(getter_AddRefs(txnMgr));
|
||||
if (txnMgr) {
|
||||
txnMgr->RemoveListener(mComposerCommandsUpdater);
|
||||
}
|
||||
DebugOnly<bool> removedTransactionListener =
|
||||
aHTMLEditor->RemoveTransactionListener(*mComposerCommandsUpdater);
|
||||
NS_WARNING_ASSERTION(removedTransactionListener,
|
||||
"Failed to remove transaction listener from the editor");
|
||||
|
||||
// Remove editor controllers from the window now that we're not
|
||||
// editing in that window any more.
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "mozilla/TextInputListener.h" // for TextInputListener
|
||||
#include "mozilla/TextServicesDocument.h" // for TextServicesDocument
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/TransactionManager.h" // for TransactionManager
|
||||
#include "mozilla/dom/CharacterData.h" // for CharacterData
|
||||
#include "mozilla/dom/Element.h" // for Element, nsINode::AsElement
|
||||
#include "mozilla/dom/HTMLBodyElement.h"
|
||||
@ -108,7 +109,6 @@
|
||||
#include "nsStyleStructFwd.h" // for nsIFrame::StyleUIReset, etc.
|
||||
#include "nsTextNode.h" // for nsTextNode
|
||||
#include "nsThreadUtils.h" // for nsRunnable
|
||||
#include "nsTransactionManager.h" // for nsTransactionManager
|
||||
#include "prtime.h" // for PR_Now
|
||||
|
||||
class nsIOutputStream;
|
||||
@ -180,7 +180,7 @@ EditorBase::~EditorBase()
|
||||
}
|
||||
// If this editor is still hiding the caret, we need to restore it.
|
||||
HideCaret(false);
|
||||
mTxnMgr = nullptr;
|
||||
mTransactionManager = nullptr;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(EditorBase)
|
||||
@ -193,7 +193,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextServicesDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextInputListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTxnMgr)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners)
|
||||
@ -223,7 +223,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextServicesDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextInputListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTxnMgr)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners)
|
||||
@ -525,9 +525,11 @@ EditorBase::PreDestroy(bool aDestroyingFrames)
|
||||
|
||||
// Transaction may grab this instance. Therefore, they should be released
|
||||
// here for stopping the circular reference with this instance.
|
||||
if (mTxnMgr) {
|
||||
mTxnMgr->Clear();
|
||||
mTxnMgr = nullptr;
|
||||
if (mTransactionManager) {
|
||||
DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
|
||||
NS_WARNING_ASSERTION(disabledUndoRedo,
|
||||
"Failed to disable undo/redo transactions");
|
||||
mTransactionManager = nullptr;
|
||||
}
|
||||
|
||||
mDidPreDestroy = true;
|
||||
@ -746,8 +748,9 @@ EditorBase::DoTransaction(Selection* aSelection, nsITransaction* aTxn)
|
||||
// We will recurse, but will not hit this case in the nested call
|
||||
DoTransaction(mPlaceholderTransaction);
|
||||
|
||||
if (mTxnMgr) {
|
||||
nsCOMPtr<nsITransaction> topTransaction = mTxnMgr->PeekUndoStack();
|
||||
if (mTransactionManager) {
|
||||
nsCOMPtr<nsITransaction> topTransaction =
|
||||
mTransactionManager->PeekUndoStack();
|
||||
nsCOMPtr<nsIAbsorbingTransaction> topAbsorbingTransaction =
|
||||
do_QueryInterface(topTransaction);
|
||||
if (topAbsorbingTransaction) {
|
||||
@ -791,9 +794,9 @@ EditorBase::DoTransaction(Selection* aSelection, nsITransaction* aTxn)
|
||||
SelectionBatcher selectionBatcher(selection);
|
||||
|
||||
nsresult rv;
|
||||
if (mTxnMgr) {
|
||||
RefPtr<nsTransactionManager> txnMgr = mTxnMgr;
|
||||
rv = txnMgr->DoTransaction(aTxn);
|
||||
if (mTransactionManager) {
|
||||
RefPtr<TransactionManager> transactionManager(mTransactionManager);
|
||||
rv = transactionManager->DoTransaction(aTxn);
|
||||
} else {
|
||||
rv = aTxn->DoTransaction();
|
||||
}
|
||||
@ -810,161 +813,65 @@ EditorBase::DoTransaction(Selection* aSelection, nsITransaction* aTxn)
|
||||
NS_IMETHODIMP
|
||||
EditorBase::EnableUndo(bool aEnable)
|
||||
{
|
||||
// XXX Should we return NS_ERROR_FAILURE if EdnableUndoRedo() or
|
||||
// DisableUndoRedo() returns false?
|
||||
if (aEnable) {
|
||||
if (!mTxnMgr) {
|
||||
mTxnMgr = new nsTransactionManager();
|
||||
}
|
||||
mTxnMgr->SetMaxTransactionCount(-1);
|
||||
} else if (mTxnMgr) {
|
||||
// disable the transaction manager if it is enabled
|
||||
mTxnMgr->Clear();
|
||||
mTxnMgr->SetMaxTransactionCount(0);
|
||||
DebugOnly<bool> enabledUndoRedo = EnableUndoRedo();
|
||||
NS_WARNING_ASSERTION(enabledUndoRedo,
|
||||
"Failed to enable undo/redo transactions");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
|
||||
NS_WARNING_ASSERTION(disabledUndoRedo,
|
||||
"Failed to disable undo/redo transactions");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::GetNumberOfUndoItems(int32_t* aNumItems)
|
||||
EditorBase::GetTransactionManager(nsITransactionManager** aTransactionManager)
|
||||
{
|
||||
*aNumItems = NumberOfUndoItems();
|
||||
return *aNumItems >= 0 ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t
|
||||
EditorBase::NumberOfUndoItems() const
|
||||
{
|
||||
if (!mTxnMgr) {
|
||||
return 0;
|
||||
}
|
||||
int32_t numItems = 0;
|
||||
if (NS_WARN_IF(NS_FAILED(mTxnMgr->GetNumberOfUndoItems(&numItems)))) {
|
||||
return -1;
|
||||
}
|
||||
return numItems;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::GetNumberOfRedoItems(int32_t* aNumItems)
|
||||
{
|
||||
*aNumItems = NumberOfRedoItems();
|
||||
return *aNumItems >= 0 ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t
|
||||
EditorBase::NumberOfRedoItems() const
|
||||
{
|
||||
if (!mTxnMgr) {
|
||||
return 0;
|
||||
}
|
||||
int32_t numItems = 0;
|
||||
if (NS_WARN_IF(NS_FAILED(mTxnMgr->GetNumberOfRedoItems(&numItems)))) {
|
||||
return -1;
|
||||
}
|
||||
return numItems;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::GetTransactionManager(nsITransactionManager** aTxnManager)
|
||||
{
|
||||
// NOTE: If you need to override this method, you need to make
|
||||
// GetTransactionManager() virtual.
|
||||
if (NS_WARN_IF(!aTxnManager)) {
|
||||
if (NS_WARN_IF(!aTransactionManager)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*aTxnManager = GetTransactionManager().take();
|
||||
if (NS_WARN_IF(!*aTxnManager)) {
|
||||
if (NS_WARN_IF(!mTransactionManager)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
NS_IF_ADDREF(*aTransactionManager = mTransactionManager);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsITransactionManager>
|
||||
EditorBase::GetTransactionManager() const
|
||||
{
|
||||
nsCOMPtr<nsITransactionManager> transactionManager = mTxnMgr.get();
|
||||
return transactionManager.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::Undo(uint32_t aCount)
|
||||
{
|
||||
ForceCompositionEnd();
|
||||
|
||||
bool hasTxnMgr, hasTransaction = false;
|
||||
CanUndo(&hasTxnMgr, &hasTransaction);
|
||||
NS_ENSURE_TRUE(hasTransaction, NS_OK);
|
||||
|
||||
AutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
|
||||
|
||||
if (!mTxnMgr) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<nsTransactionManager> txnMgr = mTxnMgr;
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
nsresult rv = txnMgr->UndoTransaction();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
DoAfterUndoTransaction();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::CanUndo(bool* aIsEnabled,
|
||||
bool* aCanUndo)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIsEnabled && aCanUndo, NS_ERROR_NULL_POINTER);
|
||||
*aIsEnabled = !!mTxnMgr;
|
||||
if (*aIsEnabled) {
|
||||
int32_t numTxns = 0;
|
||||
mTxnMgr->GetNumberOfUndoItems(&numTxns);
|
||||
*aCanUndo = !!numTxns;
|
||||
} else {
|
||||
*aCanUndo = false;
|
||||
if (NS_WARN_IF(!aIsEnabled) || NS_WARN_IF(!aCanUndo)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*aCanUndo = CanUndo();
|
||||
*aIsEnabled = IsUndoRedoEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::Redo(uint32_t aCount)
|
||||
{
|
||||
bool hasTxnMgr, hasTransaction = false;
|
||||
CanRedo(&hasTxnMgr, &hasTransaction);
|
||||
NS_ENSURE_TRUE(hasTransaction, NS_OK);
|
||||
|
||||
AutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
|
||||
|
||||
if (!mTxnMgr) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<nsTransactionManager> txnMgr = mTxnMgr;
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
nsresult rv = txnMgr->RedoTransaction();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
DoAfterRedoTransaction();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::CanRedo(bool* aIsEnabled, bool* aCanRedo)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIsEnabled && aCanRedo, NS_ERROR_NULL_POINTER);
|
||||
|
||||
*aIsEnabled = !!mTxnMgr;
|
||||
if (*aIsEnabled) {
|
||||
int32_t numTxns = 0;
|
||||
mTxnMgr->GetNumberOfRedoItems(&numTxns);
|
||||
*aCanRedo = !!numTxns;
|
||||
} else {
|
||||
*aCanRedo = false;
|
||||
if (NS_WARN_IF(!aIsEnabled) || NS_WARN_IF(!aCanRedo)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*aCanRedo = CanRedo();
|
||||
*aIsEnabled = IsUndoRedoEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -973,9 +880,9 @@ EditorBase::BeginTransaction()
|
||||
{
|
||||
BeginUpdateViewBatch();
|
||||
|
||||
if (mTxnMgr) {
|
||||
RefPtr<nsTransactionManager> txnMgr = mTxnMgr;
|
||||
txnMgr->BeginBatch(nullptr);
|
||||
if (mTransactionManager) {
|
||||
RefPtr<TransactionManager> transactionManager(mTransactionManager);
|
||||
transactionManager->BeginBatch(nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -984,9 +891,9 @@ EditorBase::BeginTransaction()
|
||||
NS_IMETHODIMP
|
||||
EditorBase::EndTransaction()
|
||||
{
|
||||
if (mTxnMgr) {
|
||||
RefPtr<nsTransactionManager> txnMgr = mTxnMgr;
|
||||
txnMgr->EndBatch(false);
|
||||
if (mTransactionManager) {
|
||||
RefPtr<TransactionManager> transactionManager(mTransactionManager);
|
||||
transactionManager->EndBatch(false);
|
||||
}
|
||||
|
||||
EndUpdateViewBatch();
|
||||
@ -2471,8 +2378,8 @@ EditorBase::EndIMEComposition()
|
||||
|
||||
// commit the IME transaction..we can get at it via the transaction mgr.
|
||||
// Note that this means IME won't work without an undo stack!
|
||||
if (mTxnMgr) {
|
||||
nsCOMPtr<nsITransaction> txn = mTxnMgr->PeekUndoStack();
|
||||
if (mTransactionManager) {
|
||||
nsCOMPtr<nsITransaction> txn = mTransactionManager->PeekUndoStack();
|
||||
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
|
||||
if (plcTxn) {
|
||||
DebugOnly<nsresult> rv = plcTxn->Commit();
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "mozilla/SelectionState.h" // for RangeUpdater, etc.
|
||||
#include "mozilla/StyleSheet.h" // for StyleSheet
|
||||
#include "mozilla/TextEditRules.h" // for TextEditRules
|
||||
#include "mozilla/TransactionManager.h" // for TransactionManager
|
||||
#include "mozilla/WeakPtr.h" // for WeakPtr
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
@ -49,9 +50,9 @@ class nsINode;
|
||||
class nsIPresShell;
|
||||
class nsISupports;
|
||||
class nsITransaction;
|
||||
class nsITransactionListener;
|
||||
class nsIWidget;
|
||||
class nsRange;
|
||||
class nsTransactionManager;
|
||||
|
||||
namespace mozilla {
|
||||
class AddStyleSheetTransaction;
|
||||
@ -1100,11 +1101,85 @@ public:
|
||||
bool ShouldHandleIMEComposition() const;
|
||||
|
||||
/**
|
||||
* Returns number of undo or redo items. If TransactionManager returns
|
||||
* unexpected error, returns -1.
|
||||
* Returns number of undo or redo items.
|
||||
*/
|
||||
int32_t NumberOfUndoItems() const;
|
||||
int32_t NumberOfRedoItems() const;
|
||||
size_t NumberOfUndoItems() const
|
||||
{
|
||||
return mTransactionManager ? mTransactionManager->NumberOfUndoItems() : 0;
|
||||
}
|
||||
size_t NumberOfRedoItems() const
|
||||
{
|
||||
return mTransactionManager ? mTransactionManager->NumberOfRedoItems() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this editor can store transactions for undo/redo.
|
||||
*/
|
||||
bool IsUndoRedoEnabled() const
|
||||
{
|
||||
return !!mTransactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if it's possible to undo/redo right now.
|
||||
*/
|
||||
bool CanUndo() const
|
||||
{
|
||||
return IsUndoRedoEnabled() && NumberOfUndoItems() > 0;
|
||||
}
|
||||
bool CanRedo() const
|
||||
{
|
||||
return IsUndoRedoEnabled() && NumberOfRedoItems() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables undo/redo feature. Returns true if it succeeded,
|
||||
* otherwise, e.g., we're undoing or redoing, returns false.
|
||||
*/
|
||||
bool EnableUndoRedo(int32_t aMaxTransactionCount = -1)
|
||||
{
|
||||
if (!mTransactionManager) {
|
||||
mTransactionManager = new TransactionManager();
|
||||
}
|
||||
return mTransactionManager->EnableUndoRedo(aMaxTransactionCount);
|
||||
}
|
||||
bool DisableUndoRedo()
|
||||
{
|
||||
if (!mTransactionManager) {
|
||||
return true;
|
||||
}
|
||||
// XXX Even we clear the transaction manager, IsUndoRedoEnabled() keep
|
||||
// returning true...
|
||||
return mTransactionManager->DisableUndoRedo();
|
||||
}
|
||||
bool ClearUndoRedo()
|
||||
{
|
||||
if (!mTransactionManager) {
|
||||
return true;
|
||||
}
|
||||
return mTransactionManager->ClearUndoRedo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes transaction listener to or from the transaction manager.
|
||||
* Note that TransactionManager does not check if the listener is in the
|
||||
* array. So, caller of AddTransactionListener() needs to manage if it's
|
||||
* already been registered to the transaction manager.
|
||||
*/
|
||||
bool AddTransactionListener(nsITransactionListener& aListener)
|
||||
{
|
||||
if (!mTransactionManager) {
|
||||
return false;
|
||||
}
|
||||
return mTransactionManager->AddTransactionListener(aListener);
|
||||
}
|
||||
bool RemoveTransactionListener(nsITransactionListener& aListener)
|
||||
{
|
||||
if (!mTransactionManager) {
|
||||
return false;
|
||||
}
|
||||
return mTransactionManager->RemoveTransactionListener(aListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* From html rules code - migration in progress.
|
||||
@ -1397,12 +1472,6 @@ public:
|
||||
return !IsInteractionAllowed() || IsMailEditor();
|
||||
}
|
||||
|
||||
/**
|
||||
* GetTransactionManager() returns transaction manager associated with the
|
||||
* editor. This may return nullptr if undo/redo hasn't been enabled.
|
||||
*/
|
||||
already_AddRefed<nsITransactionManager> GetTransactionManager() const;
|
||||
|
||||
/**
|
||||
* Get the input event target. This might return null.
|
||||
*/
|
||||
@ -1513,7 +1582,7 @@ protected:
|
||||
// Reference to text services document for mInlineSpellChecker.
|
||||
RefPtr<TextServicesDocument> mTextServicesDocument;
|
||||
|
||||
RefPtr<nsTransactionManager> mTxnMgr;
|
||||
RefPtr<TransactionManager> mTransactionManager;
|
||||
// Cached root node.
|
||||
nsCOMPtr<Element> mRootElement;
|
||||
// The form field as an event receiver.
|
||||
|
@ -68,8 +68,8 @@ UndoCommand::IsCommandEnabled(const char* aCommandName,
|
||||
if (!textEditor->IsSelectionEditable()) {
|
||||
return NS_OK;
|
||||
}
|
||||
bool isEnabled = false;
|
||||
return editor->CanUndo(&isEnabled, aIsEnabled);
|
||||
*aIsEnabled = textEditor->CanUndo();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -127,8 +127,8 @@ RedoCommand::IsCommandEnabled(const char* aCommandName,
|
||||
if (!textEditor->IsSelectionEditable()) {
|
||||
return NS_OK;
|
||||
}
|
||||
bool isEnabled = false;
|
||||
return editor->CanRedo(&isEnabled, aIsEnabled);
|
||||
*aIsEnabled = textEditor->CanRedo();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -195,8 +195,10 @@ ClearUndoCommand::DoCommand(const char* aCommandName,
|
||||
}
|
||||
TextEditor* textEditor = editor->AsTextEditor();
|
||||
MOZ_ASSERT(textEditor);
|
||||
textEditor->EnableUndo(false); // Turning off undo clears undo/redo stacks.
|
||||
textEditor->EnableUndo(true); // This re-enables undo/redo.
|
||||
// XXX Should we return NS_ERROR_FAILURE if ClearUndoRedo() returns false?
|
||||
DebugOnly<bool> clearedUndoRedo = textEditor->ClearUndoRedo();
|
||||
NS_WARNING_ASSERTION(clearedUndoRedo,
|
||||
"Failed to clear undo/redo transactions");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -212,8 +212,8 @@ TextEditor::EndEditorInit()
|
||||
}
|
||||
// Throw away the old transaction manager if this is not the first time that
|
||||
// we're initializing the editor.
|
||||
EnableUndo(false);
|
||||
EnableUndo(true);
|
||||
ClearUndoRedo();
|
||||
EnableUndoRedo();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1134,25 +1134,50 @@ TextEditor::SetNewlineHandling(int32_t aNewlineHandling)
|
||||
NS_IMETHODIMP
|
||||
TextEditor::Undo(uint32_t aCount)
|
||||
{
|
||||
// Protect the edit rules object from dying
|
||||
// If we don't have transaction in the undo stack, we shouldn't notify
|
||||
// anybody of trying to undo since it's not useful notification but we
|
||||
// need to pay some runtime cost.
|
||||
if (!CanUndo()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If there is composition, we shouldn't allow to undo with committing
|
||||
// composition since Chrome doesn't allow it and it doesn't make sense
|
||||
// because committing composition causes one transaction and Undo(1)
|
||||
// undoes the committing composition.
|
||||
if (GetComposition()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Protect the edit rules object from dying.
|
||||
RefPtr<TextEditRules> rules(mRules);
|
||||
|
||||
AutoUpdateViewBatch beginViewBatching(this);
|
||||
|
||||
CommitComposition();
|
||||
|
||||
NotifyEditorObservers(eNotifyEditorObserversOfBefore);
|
||||
if (NS_WARN_IF(!CanUndo()) || NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
AutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
|
||||
nsresult rv;
|
||||
{
|
||||
AutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
|
||||
|
||||
RulesInfo ruleInfo(EditAction::undo);
|
||||
RefPtr<Selection> selection = GetSelection();
|
||||
bool cancel, handled;
|
||||
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
|
||||
if (!cancel && NS_SUCCEEDED(rv)) {
|
||||
rv = EditorBase::Undo(aCount);
|
||||
rv = rules->DidDoAction(selection, &ruleInfo, rv);
|
||||
RulesInfo ruleInfo(EditAction::undo);
|
||||
RefPtr<Selection> selection = GetSelection();
|
||||
bool cancel, handled;
|
||||
rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
if (!cancel && NS_SUCCEEDED(rv)) {
|
||||
RefPtr<TransactionManager> transactionManager(mTransactionManager);
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
rv = transactionManager->Undo();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
DoAfterUndoTransaction();
|
||||
}
|
||||
rv = rules->DidDoAction(selection, &ruleInfo, rv);
|
||||
}
|
||||
}
|
||||
|
||||
NotifyEditorObservers(eNotifyEditorObserversOfEnd);
|
||||
@ -1162,25 +1187,50 @@ TextEditor::Undo(uint32_t aCount)
|
||||
NS_IMETHODIMP
|
||||
TextEditor::Redo(uint32_t aCount)
|
||||
{
|
||||
// Protect the edit rules object from dying
|
||||
// If we don't have transaction in the redo stack, we shouldn't notify
|
||||
// anybody of trying to redo since it's not useful notification but we
|
||||
// need to pay some runtime cost.
|
||||
if (!CanRedo()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If there is composition, we shouldn't allow to redo with committing
|
||||
// composition since Chrome doesn't allow it and it doesn't make sense
|
||||
// because committing composition causes removing all transactions from
|
||||
// the redo queue. So, it becomes impossible to redo anything.
|
||||
if (GetComposition()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Protect the edit rules object from dying.
|
||||
RefPtr<TextEditRules> rules(mRules);
|
||||
|
||||
AutoUpdateViewBatch beginViewBatching(this);
|
||||
|
||||
CommitComposition();
|
||||
|
||||
NotifyEditorObservers(eNotifyEditorObserversOfBefore);
|
||||
if (NS_WARN_IF(!CanRedo()) || NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
AutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
|
||||
nsresult rv;
|
||||
{
|
||||
AutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
|
||||
|
||||
RulesInfo ruleInfo(EditAction::redo);
|
||||
RefPtr<Selection> selection = GetSelection();
|
||||
bool cancel, handled;
|
||||
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
|
||||
if (!cancel && NS_SUCCEEDED(rv)) {
|
||||
rv = EditorBase::Redo(aCount);
|
||||
rv = rules->DidDoAction(selection, &ruleInfo, rv);
|
||||
RulesInfo ruleInfo(EditAction::redo);
|
||||
RefPtr<Selection> selection = GetSelection();
|
||||
bool cancel, handled;
|
||||
rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
if (!cancel && NS_SUCCEEDED(rv)) {
|
||||
RefPtr<TransactionManager> transactionManager(mTransactionManager);
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
nsresult rv = transactionManager->Redo();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
DoAfterRedoTransaction();
|
||||
}
|
||||
rv = rules->DidDoAction(selection, &ruleInfo, rv);
|
||||
}
|
||||
}
|
||||
|
||||
NotifyEditorObservers(eNotifyEditorObserversOfEnd);
|
||||
|
@ -86,8 +86,10 @@ public:
|
||||
|
||||
NS_IMETHOD SetDocumentCharacterSet(const nsACString& characterSet) override;
|
||||
|
||||
NS_IMETHOD Undo(uint32_t aCount) override;
|
||||
NS_IMETHOD Redo(uint32_t aCount) override;
|
||||
// If there are some good name to create non-virtual Undo()/Redo() methods,
|
||||
// we should create them and those methods should just run them.
|
||||
NS_IMETHOD Undo(uint32_t aCount) final;
|
||||
NS_IMETHOD Redo(uint32_t aCount) final;
|
||||
|
||||
NS_IMETHOD Cut() override;
|
||||
NS_IMETHOD CanCut(bool* aCanCut) override;
|
||||
|
@ -77,7 +77,6 @@ UNIFIED_SOURCES += [
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/editor/txmgr',
|
||||
'/extensions/spellcheck/src',
|
||||
'/layout/generic',
|
||||
'/layout/style',
|
||||
|
@ -169,16 +169,6 @@ interface nsIEditor : nsISupports
|
||||
*/
|
||||
void enableUndo(in boolean enable);
|
||||
|
||||
/**
|
||||
* The number of items on the undo stack.
|
||||
*/
|
||||
readonly attribute long numberOfUndoItems;
|
||||
|
||||
/**
|
||||
* The number of items on the redo stack.
|
||||
*/
|
||||
readonly attribute long numberOfRedoItems;
|
||||
|
||||
/** undo reverses the effects of the last Do operation,
|
||||
* if Undo is enabled in the editor.
|
||||
* It is provided here so clients need no knowledge of whether
|
||||
|
@ -3,29 +3,34 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TransactionItem.h"
|
||||
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "mozilla/TransactionManager.h"
|
||||
#include "mozilla/TransactionStack.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsError.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsITransaction.h"
|
||||
#include "nsTransactionItem.h"
|
||||
#include "nsTransactionManager.h"
|
||||
#include "nsTransactionStack.h"
|
||||
|
||||
nsTransactionItem::nsTransactionItem(nsITransaction *aTransaction)
|
||||
: mTransaction(aTransaction), mUndoStack(0), mRedoStack(0)
|
||||
namespace mozilla {
|
||||
|
||||
TransactionItem::TransactionItem(nsITransaction* aTransaction)
|
||||
: mTransaction(aTransaction)
|
||||
, mUndoStack(0)
|
||||
, mRedoStack(0)
|
||||
{
|
||||
}
|
||||
|
||||
nsTransactionItem::~nsTransactionItem()
|
||||
TransactionItem::~TransactionItem()
|
||||
{
|
||||
delete mRedoStack;
|
||||
delete mUndoStack;
|
||||
}
|
||||
|
||||
void
|
||||
nsTransactionItem::CleanUp()
|
||||
TransactionItem::CleanUp()
|
||||
{
|
||||
mData.Clear();
|
||||
mTransaction = nullptr;
|
||||
@ -37,17 +42,17 @@ nsTransactionItem::CleanUp()
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(nsTransactionItem)
|
||||
NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(nsTransactionItem,
|
||||
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(TransactionItem)
|
||||
NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(TransactionItem,
|
||||
CleanUp())
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionItem)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(TransactionItem)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionItem)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TransactionItem)
|
||||
tmp->CleanUp();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionItem)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TransactionItem)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
|
||||
if (tmp->mRedoStack) {
|
||||
@ -58,16 +63,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionItem)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTransactionItem, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTransactionItem, Release)
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(TransactionItem, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(TransactionItem, Release)
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::AddChild(nsTransactionItem *aTransactionItem)
|
||||
TransactionItem::AddChild(TransactionItem* aTransactionItem)
|
||||
{
|
||||
NS_ENSURE_TRUE(aTransactionItem, NS_ERROR_NULL_POINTER);
|
||||
|
||||
if (!mUndoStack) {
|
||||
mUndoStack = new nsTransactionStack(nsTransactionStack::FOR_UNDO);
|
||||
mUndoStack = new TransactionStack(TransactionStack::FOR_UNDO);
|
||||
}
|
||||
|
||||
mUndoStack->Push(aTransactionItem);
|
||||
@ -75,14 +80,14 @@ nsTransactionItem::AddChild(nsTransactionItem *aTransactionItem)
|
||||
}
|
||||
|
||||
already_AddRefed<nsITransaction>
|
||||
nsTransactionItem::GetTransaction()
|
||||
TransactionItem::GetTransaction()
|
||||
{
|
||||
nsCOMPtr<nsITransaction> txn = mTransaction;
|
||||
return txn.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::GetIsBatch(bool *aIsBatch)
|
||||
TransactionItem::GetIsBatch(bool* aIsBatch)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIsBatch, NS_ERROR_NULL_POINTER);
|
||||
*aIsBatch = !mTransaction;
|
||||
@ -90,7 +95,7 @@ nsTransactionItem::GetIsBatch(bool *aIsBatch)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::GetNumberOfChildren(int32_t *aNumChildren)
|
||||
TransactionItem::GetNumberOfChildren(int32_t* aNumChildren)
|
||||
{
|
||||
NS_ENSURE_TRUE(aNumChildren, NS_ERROR_NULL_POINTER);
|
||||
|
||||
@ -109,7 +114,8 @@ nsTransactionItem::GetNumberOfChildren(int32_t *aNumChildren)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
|
||||
TransactionItem::GetChild(int32_t aIndex,
|
||||
TransactionItem** aChild)
|
||||
{
|
||||
NS_ENSURE_TRUE(aChild, NS_ERROR_NULL_POINTER);
|
||||
|
||||
@ -132,7 +138,7 @@ nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
|
||||
if (numItems > 0 && aIndex < numItems) {
|
||||
NS_ENSURE_TRUE(mUndoStack, NS_ERROR_FAILURE);
|
||||
|
||||
RefPtr<nsTransactionItem> child = mUndoStack->GetItem(aIndex);
|
||||
RefPtr<TransactionItem> child = mUndoStack->GetItem(aIndex);
|
||||
child.forget(aChild);
|
||||
return *aChild ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -144,13 +150,13 @@ nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(mRedoStack && numItems != 0 && aIndex < numItems, NS_ERROR_FAILURE);
|
||||
|
||||
RefPtr<nsTransactionItem> child = mRedoStack->GetItem(aIndex);
|
||||
RefPtr<TransactionItem> child = mRedoStack->GetItem(aIndex);
|
||||
child.forget(aChild);
|
||||
return *aChild ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::DoTransaction()
|
||||
TransactionItem::DoTransaction()
|
||||
{
|
||||
if (mTransaction) {
|
||||
return mTransaction->DoTransaction();
|
||||
@ -159,11 +165,11 @@ nsTransactionItem::DoTransaction()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
|
||||
TransactionItem::UndoTransaction(TransactionManager* aTransactionManager)
|
||||
{
|
||||
nsresult rv = UndoChildren(aTxMgr);
|
||||
nsresult rv = UndoChildren(aTransactionManager);
|
||||
if (NS_FAILED(rv)) {
|
||||
RecoverFromUndoError(aTxMgr);
|
||||
RecoverFromUndoError(aTransactionManager);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -173,7 +179,7 @@ nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
|
||||
|
||||
rv = mTransaction->UndoTransaction();
|
||||
if (NS_FAILED(rv)) {
|
||||
RecoverFromUndoError(aTxMgr);
|
||||
RecoverFromUndoError(aTransactionManager);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -181,11 +187,11 @@ nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
||||
TransactionItem::UndoChildren(TransactionManager* aTransactionManager)
|
||||
{
|
||||
if (mUndoStack) {
|
||||
if (!mRedoStack && mUndoStack) {
|
||||
mRedoStack = new nsTransactionStack(nsTransactionStack::FOR_REDO);
|
||||
mRedoStack = new TransactionStack(TransactionStack::FOR_REDO);
|
||||
}
|
||||
|
||||
/* Undo all of the transaction items children! */
|
||||
@ -193,14 +199,14 @@ nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
while (sz-- > 0) {
|
||||
RefPtr<nsTransactionItem> item = mUndoStack->Peek();
|
||||
if (!item) {
|
||||
RefPtr<TransactionItem> transactionItem = mUndoStack->Peek();
|
||||
if (!transactionItem) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransaction> t = item->GetTransaction();
|
||||
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
|
||||
bool doInterrupt = false;
|
||||
rv = aTxMgr->WillUndoNotify(t, &doInterrupt);
|
||||
rv = aTransactionManager->WillUndoNotify(transaction, &doInterrupt);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -208,13 +214,13 @@ nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = item->UndoTransaction(aTxMgr);
|
||||
rv = transactionItem->UndoTransaction(aTransactionManager);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
item = mUndoStack->Pop();
|
||||
mRedoStack->Push(item.forget());
|
||||
transactionItem = mUndoStack->Pop();
|
||||
mRedoStack->Push(transactionItem.forget());
|
||||
}
|
||||
|
||||
nsresult rv2 = aTxMgr->DidUndoNotify(t, rv);
|
||||
nsresult rv2 = aTransactionManager->DidUndoNotify(transaction, rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
@ -229,7 +235,7 @@ nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
|
||||
TransactionItem::RedoTransaction(TransactionManager* aTransactionManager)
|
||||
{
|
||||
nsCOMPtr<nsITransaction> transaction(mTransaction);
|
||||
if (transaction) {
|
||||
@ -237,9 +243,9 @@ nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsresult rv = RedoChildren(aTxMgr);
|
||||
nsresult rv = RedoChildren(aTransactionManager);
|
||||
if (NS_FAILED(rv)) {
|
||||
RecoverFromRedoError(aTxMgr);
|
||||
RecoverFromRedoError(aTransactionManager);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -247,7 +253,7 @@ nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
|
||||
TransactionItem::RedoChildren(TransactionManager* aTransactionManager)
|
||||
{
|
||||
if (!mRedoStack) {
|
||||
return NS_OK;
|
||||
@ -258,14 +264,14 @@ nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
while (sz-- > 0) {
|
||||
RefPtr<nsTransactionItem> item = mRedoStack->Peek();
|
||||
if (!item) {
|
||||
RefPtr<TransactionItem> transactionItem = mRedoStack->Peek();
|
||||
if (!transactionItem) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransaction> t = item->GetTransaction();
|
||||
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
|
||||
bool doInterrupt = false;
|
||||
rv = aTxMgr->WillRedoNotify(t, &doInterrupt);
|
||||
rv = aTransactionManager->WillRedoNotify(transaction, &doInterrupt);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -273,14 +279,14 @@ nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = item->RedoTransaction(aTxMgr);
|
||||
rv = transactionItem->RedoTransaction(aTransactionManager);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
item = mRedoStack->Pop();
|
||||
mUndoStack->Push(item.forget());
|
||||
transactionItem = mRedoStack->Pop();
|
||||
mUndoStack->Push(transactionItem.forget());
|
||||
}
|
||||
|
||||
// XXX Shouldn't this DidRedoNotify()? (bug 1311626)
|
||||
nsresult rv2 = aTxMgr->DidUndoNotify(t, rv);
|
||||
nsresult rv2 = aTransactionManager->DidUndoNotify(transaction, rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
@ -292,7 +298,7 @@ nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::GetNumberOfUndoItems(int32_t *aNumItems)
|
||||
TransactionItem::GetNumberOfUndoItems(int32_t* aNumItems)
|
||||
{
|
||||
NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
|
||||
|
||||
@ -306,7 +312,7 @@ nsTransactionItem::GetNumberOfUndoItems(int32_t *aNumItems)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::GetNumberOfRedoItems(int32_t *aNumItems)
|
||||
TransactionItem::GetNumberOfRedoItems(int32_t* aNumItems)
|
||||
{
|
||||
NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
|
||||
|
||||
@ -320,22 +326,22 @@ nsTransactionItem::GetNumberOfRedoItems(int32_t *aNumItems)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::RecoverFromUndoError(nsTransactionManager *aTxMgr)
|
||||
TransactionItem::RecoverFromUndoError(TransactionManager* aTransactionManager)
|
||||
{
|
||||
// If this method gets called, we never got to the point where we
|
||||
// successfully called UndoTransaction() for the transaction item itself.
|
||||
// Just redo any children that successfully called undo!
|
||||
return RedoChildren(aTxMgr);
|
||||
return RedoChildren(aTransactionManager);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr)
|
||||
TransactionItem::RecoverFromRedoError(TransactionManager* aTransactionManager)
|
||||
{
|
||||
// If this method gets called, we already successfully called
|
||||
// RedoTransaction() for the transaction item itself. Undo all
|
||||
// the children that successfully called RedoTransaction(),
|
||||
// then undo the transaction item itself.
|
||||
nsresult rv = UndoChildren(aTxMgr);
|
||||
nsresult rv = UndoChildren(aTransactionManager);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -347,3 +353,4 @@ nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr)
|
||||
return mTransaction->UndoTransaction();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
71
editor/txmgr/TransactionItem.h
Normal file
71
editor/txmgr/TransactionItem.h
Normal file
@ -0,0 +1,71 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TransactionItem_h
|
||||
#define TransactionItem_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nscore.h"
|
||||
|
||||
class nsITransaction;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class TransactionManager;
|
||||
class TransactionStack;
|
||||
|
||||
class TransactionItem final
|
||||
{
|
||||
public:
|
||||
explicit TransactionItem(nsITransaction* aTransaction);
|
||||
NS_METHOD_(MozExternalRefCountType) AddRef();
|
||||
NS_METHOD_(MozExternalRefCountType) Release();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(TransactionItem)
|
||||
|
||||
nsresult AddChild(TransactionItem* aTransactionItem);
|
||||
already_AddRefed<nsITransaction> GetTransaction();
|
||||
nsresult GetIsBatch(bool *aIsBatch);
|
||||
nsresult GetNumberOfChildren(int32_t *aNumChildren);
|
||||
nsresult GetChild(int32_t aIndex, TransactionItem** aChild);
|
||||
|
||||
nsresult DoTransaction();
|
||||
nsresult UndoTransaction(TransactionManager* aTransactionManager);
|
||||
nsresult RedoTransaction(TransactionManager* aTransactionManager);
|
||||
|
||||
nsCOMArray<nsISupports>& GetData()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
private:
|
||||
nsresult UndoChildren(TransactionManager* aTransactionManager);
|
||||
nsresult RedoChildren(TransactionManager* aTransactionManager);
|
||||
|
||||
nsresult RecoverFromUndoError(TransactionManager* aTransactionManager);
|
||||
nsresult RecoverFromRedoError(TransactionManager* aTransactionManager);
|
||||
|
||||
nsresult GetNumberOfUndoItems(int32_t* aNumItems);
|
||||
nsresult GetNumberOfRedoItems(int32_t* aNumItems);
|
||||
|
||||
void CleanUp();
|
||||
|
||||
~TransactionItem();
|
||||
|
||||
nsCycleCollectingAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
nsCOMArray<nsISupports> mData;
|
||||
nsCOMPtr<nsITransaction> mTransaction;
|
||||
TransactionStack* mUndoStack;
|
||||
TransactionStack* mRedoStack;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef TransactionItem_h
|
@ -3,8 +3,11 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/TransactionManager.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "mozilla/TransactionStack.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsError.h"
|
||||
@ -13,49 +16,45 @@
|
||||
#include "nsITransaction.h"
|
||||
#include "nsITransactionListener.h"
|
||||
#include "nsIWeakReference.h"
|
||||
#include "nsTransactionItem.h"
|
||||
#include "nsTransactionManager.h"
|
||||
#include "nsTransactionStack.h"
|
||||
#include "TransactionItem.h"
|
||||
|
||||
nsTransactionManager::nsTransactionManager(int32_t aMaxTransactionCount)
|
||||
namespace mozilla {
|
||||
|
||||
TransactionManager::TransactionManager(int32_t aMaxTransactionCount)
|
||||
: mMaxTransactionCount(aMaxTransactionCount)
|
||||
, mDoStack(nsTransactionStack::FOR_UNDO)
|
||||
, mUndoStack(nsTransactionStack::FOR_UNDO)
|
||||
, mRedoStack(nsTransactionStack::FOR_REDO)
|
||||
, mDoStack(TransactionStack::FOR_UNDO)
|
||||
, mUndoStack(TransactionStack::FOR_UNDO)
|
||||
, mRedoStack(TransactionStack::FOR_REDO)
|
||||
{
|
||||
}
|
||||
|
||||
nsTransactionManager::~nsTransactionManager()
|
||||
{
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(TransactionManager)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionManager)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
|
||||
tmp->mDoStack.DoUnlink();
|
||||
tmp->mUndoStack.DoUnlink();
|
||||
tmp->mRedoStack.DoUnlink();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
|
||||
tmp->mDoStack.DoTraverse(cb);
|
||||
tmp->mUndoStack.DoTraverse(cb);
|
||||
tmp->mRedoStack.DoTraverse(cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTransactionManager)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransactionManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITransactionManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITransactionManager)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TransactionManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TransactionManager)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
|
||||
TransactionManager::DoTransaction(nsITransaction* aTransaction)
|
||||
{
|
||||
NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
|
||||
|
||||
@ -88,26 +87,33 @@ nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::UndoTransaction()
|
||||
TransactionManager::UndoTransaction()
|
||||
{
|
||||
// It is illegal to call UndoTransaction() while the transaction manager is
|
||||
// executing a transaction's DoTransaction() method! If this happens,
|
||||
// the UndoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
|
||||
return Undo();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TransactionManager::Undo()
|
||||
{
|
||||
// It's possible to be called Undo() again while the transaction manager is
|
||||
// executing a transaction's DoTransaction() method. If this happens,
|
||||
// the Undo() request is ignored, and we return NS_ERROR_FAILURE. This
|
||||
// may occur if a mutation event listener calls document.execCommand("undo").
|
||||
if (!mDoStack.IsEmpty()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Peek at the top of the undo stack. Don't remove the transaction
|
||||
// until it has successfully completed.
|
||||
RefPtr<nsTransactionItem> tx = mUndoStack.Peek();
|
||||
if (!tx) {
|
||||
RefPtr<TransactionItem> transactionItem = mUndoStack.Peek();
|
||||
if (!transactionItem) {
|
||||
// Bail if there's nothing on the stack.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransaction> t = tx->GetTransaction();
|
||||
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
|
||||
bool doInterrupt = false;
|
||||
nsresult rv = WillUndoNotify(t, &doInterrupt);
|
||||
nsresult rv = WillUndoNotify(transaction, &doInterrupt);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -115,13 +121,13 @@ nsTransactionManager::UndoTransaction()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = tx->UndoTransaction(this);
|
||||
rv = transactionItem->UndoTransaction(this);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
tx = mUndoStack.Pop();
|
||||
mRedoStack.Push(tx.forget());
|
||||
transactionItem = mUndoStack.Pop();
|
||||
mRedoStack.Push(transactionItem.forget());
|
||||
}
|
||||
|
||||
nsresult rv2 = DidUndoNotify(t, rv);
|
||||
nsresult rv2 = DidUndoNotify(transaction, rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
@ -132,26 +138,33 @@ nsTransactionManager::UndoTransaction()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::RedoTransaction()
|
||||
TransactionManager::RedoTransaction()
|
||||
{
|
||||
// It is illegal to call RedoTransaction() while the transaction manager is
|
||||
// executing a transaction's DoTransaction() method! If this happens,
|
||||
// the RedoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
|
||||
return Redo();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TransactionManager::Redo()
|
||||
{
|
||||
// It's possible to be called Redo() again while the transaction manager is
|
||||
// executing a transaction's DoTransaction() method. If this happens,
|
||||
// the Redo() request is ignored, and we return NS_ERROR_FAILURE. This
|
||||
// may occur if a mutation event listener calls document.execCommand("redo").
|
||||
if (!mDoStack.IsEmpty()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Peek at the top of the redo stack. Don't remove the transaction
|
||||
// until it has successfully completed.
|
||||
RefPtr<nsTransactionItem> tx = mRedoStack.Peek();
|
||||
if (!tx) {
|
||||
RefPtr<TransactionItem> transactionItem = mRedoStack.Peek();
|
||||
if (!transactionItem) {
|
||||
// Bail if there's nothing on the stack.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransaction> t = tx->GetTransaction();
|
||||
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
|
||||
bool doInterrupt = false;
|
||||
nsresult rv = WillRedoNotify(t, &doInterrupt);
|
||||
nsresult rv = WillRedoNotify(transaction, &doInterrupt);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -159,13 +172,13 @@ nsTransactionManager::RedoTransaction()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = tx->RedoTransaction(this);
|
||||
rv = transactionItem->RedoTransaction(this);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
tx = mRedoStack.Pop();
|
||||
mUndoStack.Push(tx.forget());
|
||||
transactionItem = mRedoStack.Pop();
|
||||
mUndoStack.Push(transactionItem.forget());
|
||||
}
|
||||
|
||||
nsresult rv2 = DidRedoNotify(t, rv);
|
||||
nsresult rv2 = DidRedoNotify(transaction, rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
@ -176,17 +189,13 @@ nsTransactionManager::RedoTransaction()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::Clear()
|
||||
TransactionManager::Clear()
|
||||
{
|
||||
nsresult rv = ClearRedoStack();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return ClearUndoStack();
|
||||
return ClearUndoRedo() ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::BeginBatch(nsISupports* aData)
|
||||
TransactionManager::BeginBatch(nsISupports* aData)
|
||||
{
|
||||
// We can batch independent transactions together by simply pushing
|
||||
// a dummy transaction item on the do stack. This dummy transaction item
|
||||
@ -214,7 +223,7 @@ nsTransactionManager::BeginBatch(nsISupports* aData)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::EndBatch(bool aAllowEmpty)
|
||||
TransactionManager::EndBatch(bool aAllowEmpty)
|
||||
{
|
||||
// XXX: Need to add some mechanism to detect the case where the transaction
|
||||
// at the top of the do stack isn't the dummy transaction, so we can
|
||||
@ -226,12 +235,12 @@ nsTransactionManager::EndBatch(bool aAllowEmpty)
|
||||
// transaction, it should be nullptr. This may not be true in the
|
||||
// future when we allow users to execute a transaction when beginning
|
||||
// a batch!!!!
|
||||
RefPtr<nsTransactionItem> tx = mDoStack.Peek();
|
||||
nsCOMPtr<nsITransaction> ti;
|
||||
if (tx) {
|
||||
ti = tx->GetTransaction();
|
||||
RefPtr<TransactionItem> transactionItem = mDoStack.Peek();
|
||||
if (!transactionItem) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!tx || ti) {
|
||||
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
|
||||
if (transaction) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -256,21 +265,23 @@ nsTransactionManager::EndBatch(bool aAllowEmpty)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::GetNumberOfUndoItems(int32_t *aNumItems)
|
||||
TransactionManager::GetNumberOfUndoItems(int32_t* aNumItems)
|
||||
{
|
||||
*aNumItems = mUndoStack.GetSize();
|
||||
*aNumItems = static_cast<int32_t>(NumberOfUndoItems());
|
||||
MOZ_ASSERT(*aNumItems >= 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::GetNumberOfRedoItems(int32_t *aNumItems)
|
||||
TransactionManager::GetNumberOfRedoItems(int32_t* aNumItems)
|
||||
{
|
||||
*aNumItems = mRedoStack.GetSize();
|
||||
*aNumItems = static_cast<int32_t>(NumberOfRedoItems());
|
||||
MOZ_ASSERT(*aNumItems >= 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::GetMaxTransactionCount(int32_t *aMaxCount)
|
||||
TransactionManager::GetMaxTransactionCount(int32_t* aMaxCount)
|
||||
{
|
||||
NS_ENSURE_TRUE(aMaxCount, NS_ERROR_NULL_POINTER);
|
||||
*aMaxCount = mMaxTransactionCount;
|
||||
@ -278,61 +289,79 @@ nsTransactionManager::GetMaxTransactionCount(int32_t *aMaxCount)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::SetMaxTransactionCount(int32_t aMaxCount)
|
||||
TransactionManager::SetMaxTransactionCount(int32_t aMaxCount)
|
||||
{
|
||||
// It is illegal to call SetMaxTransactionCount() while the transaction
|
||||
// manager is executing a transaction's DoTransaction() method because
|
||||
// the undo and redo stacks might get pruned! If this happens, the
|
||||
// SetMaxTransactionCount() request is ignored, and we return
|
||||
// NS_ERROR_FAILURE.
|
||||
if (!mDoStack.IsEmpty()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return EnableUndoRedo(aMaxCount) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool
|
||||
TransactionManager::EnableUndoRedo(int32_t aMaxTransactionCount)
|
||||
{
|
||||
// It is illegal to call EnableUndoRedo() while the transaction manager is
|
||||
// executing a transaction's DoTransaction() method because the undo and redo
|
||||
// stacks might get pruned. If this happens, the EnableUndoRedo() request is
|
||||
// ignored, and we return false.
|
||||
if (NS_WARN_IF(!mDoStack.IsEmpty())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If aMaxCount is less than zero, the user wants unlimited
|
||||
// levels of undo! No need to prune the undo or redo stacks!
|
||||
if (aMaxCount < 0) {
|
||||
// If aMaxTransactionCount is 0, it means to disable undo/redo.
|
||||
if (!aMaxTransactionCount) {
|
||||
mUndoStack.Clear();
|
||||
mRedoStack.Clear();
|
||||
mMaxTransactionCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If aMaxTransactionCount is less than zero, the user wants unlimited
|
||||
// levels of undo! No need to prune the undo or redo stacks.
|
||||
if (aMaxTransactionCount < 0) {
|
||||
mMaxTransactionCount = -1;
|
||||
return NS_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If aMaxCount is greater than the number of transactions that currently
|
||||
// exist on the undo and redo stack, there is no need to prune the
|
||||
// undo or redo stacks!
|
||||
int32_t numUndoItems = mUndoStack.GetSize();
|
||||
int32_t numRedoItems = mRedoStack.GetSize();
|
||||
int32_t total = numUndoItems + numRedoItems;
|
||||
if (aMaxCount > total) {
|
||||
mMaxTransactionCount = aMaxCount;
|
||||
return NS_OK;
|
||||
// If new max transaction count is greater than or equal to current max
|
||||
// transaction count, we don't need to remove any transactions.
|
||||
if (mMaxTransactionCount >= 0 &&
|
||||
mMaxTransactionCount <= aMaxTransactionCount) {
|
||||
mMaxTransactionCount = aMaxTransactionCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If aMaxTransactionCount is greater than the number of transactions that
|
||||
// currently exist on the undo and redo stack, there is no need to prune the
|
||||
// undo or redo stacks.
|
||||
size_t numUndoItems = NumberOfUndoItems();
|
||||
size_t numRedoItems = NumberOfRedoItems();
|
||||
size_t total = numUndoItems + numRedoItems;
|
||||
size_t newMaxTransactionCount = static_cast<size_t>(aMaxTransactionCount);
|
||||
if (newMaxTransactionCount > total) {
|
||||
mMaxTransactionCount = aMaxTransactionCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try getting rid of some transactions on the undo stack! Start at
|
||||
// the bottom of the stack and pop towards the top.
|
||||
while (numUndoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
|
||||
RefPtr<nsTransactionItem> tx = mUndoStack.PopBottom();
|
||||
if (!tx) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
--numUndoItems;
|
||||
for (; numUndoItems && (numRedoItems + numUndoItems) > newMaxTransactionCount;
|
||||
numUndoItems--) {
|
||||
RefPtr<TransactionItem> transactionItem = mUndoStack.PopBottom();
|
||||
MOZ_ASSERT(transactionItem);
|
||||
}
|
||||
|
||||
// If necessary, get rid of some transactions on the redo stack! Start at
|
||||
// the bottom of the stack and pop towards the top.
|
||||
while (numRedoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
|
||||
RefPtr<nsTransactionItem> tx = mRedoStack.PopBottom();
|
||||
if (!tx) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
--numRedoItems;
|
||||
for (; numRedoItems && (numRedoItems + numUndoItems) > newMaxTransactionCount;
|
||||
numRedoItems--) {
|
||||
RefPtr<TransactionItem> transactionItem = mRedoStack.PopBottom();
|
||||
MOZ_ASSERT(transactionItem);
|
||||
}
|
||||
|
||||
mMaxTransactionCount = aMaxCount;
|
||||
return NS_OK;
|
||||
mMaxTransactionCount = aMaxTransactionCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::PeekUndoStack(nsITransaction **aTransaction)
|
||||
TransactionManager::PeekUndoStack(nsITransaction** aTransaction)
|
||||
{
|
||||
MOZ_ASSERT(aTransaction);
|
||||
*aTransaction = PeekUndoStack().take();
|
||||
@ -340,17 +369,17 @@ nsTransactionManager::PeekUndoStack(nsITransaction **aTransaction)
|
||||
}
|
||||
|
||||
already_AddRefed<nsITransaction>
|
||||
nsTransactionManager::PeekUndoStack()
|
||||
TransactionManager::PeekUndoStack()
|
||||
{
|
||||
RefPtr<nsTransactionItem> tx = mUndoStack.Peek();
|
||||
if (!tx) {
|
||||
RefPtr<TransactionItem> transactionItem = mUndoStack.Peek();
|
||||
if (!transactionItem) {
|
||||
return nullptr;
|
||||
}
|
||||
return tx->GetTransaction();
|
||||
return transactionItem->GetTransaction();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::PeekRedoStack(nsITransaction** aTransaction)
|
||||
TransactionManager::PeekRedoStack(nsITransaction** aTransaction)
|
||||
{
|
||||
MOZ_ASSERT(aTransaction);
|
||||
*aTransaction = PeekRedoStack().take();
|
||||
@ -358,30 +387,27 @@ nsTransactionManager::PeekRedoStack(nsITransaction** aTransaction)
|
||||
}
|
||||
|
||||
already_AddRefed<nsITransaction>
|
||||
nsTransactionManager::PeekRedoStack()
|
||||
TransactionManager::PeekRedoStack()
|
||||
{
|
||||
RefPtr<nsTransactionItem> tx = mRedoStack.Peek();
|
||||
if (!tx) {
|
||||
RefPtr<TransactionItem> transactionItem = mRedoStack.Peek();
|
||||
if (!transactionItem) {
|
||||
return nullptr;
|
||||
}
|
||||
return tx->GetTransaction();
|
||||
return transactionItem->GetTransaction();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::BatchTopUndo()
|
||||
TransactionManager::BatchTopUndo()
|
||||
{
|
||||
if (mUndoStack.GetSize() < 2) {
|
||||
// Not enough transactions to merge into one batch.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<nsTransactionItem> lastUndo;
|
||||
RefPtr<nsTransactionItem> previousUndo;
|
||||
|
||||
lastUndo = mUndoStack.Pop();
|
||||
RefPtr<TransactionItem> lastUndo = mUndoStack.Pop();
|
||||
MOZ_ASSERT(lastUndo, "There should be at least two transactions.");
|
||||
|
||||
previousUndo = mUndoStack.Peek();
|
||||
RefPtr<TransactionItem> previousUndo = mUndoStack.Peek();
|
||||
MOZ_ASSERT(previousUndo, "There should be at least two transactions.");
|
||||
|
||||
nsresult rv = previousUndo->AddChild(lastUndo);
|
||||
@ -396,46 +422,57 @@ nsTransactionManager::BatchTopUndo()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::RemoveTopUndo()
|
||||
TransactionManager::RemoveTopUndo()
|
||||
{
|
||||
if (mUndoStack.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<nsTransactionItem> lastUndo = mUndoStack.Pop();
|
||||
RefPtr<TransactionItem> lastUndo = mUndoStack.Pop();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::AddListener(nsITransactionListener *aListener)
|
||||
TransactionManager::AddListener(nsITransactionListener* aListener)
|
||||
{
|
||||
NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
|
||||
return mListeners.AppendObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
|
||||
if (NS_WARN_IF(!aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return AddTransactionListener(*aListener) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::RemoveListener(nsITransactionListener *aListener)
|
||||
TransactionManager::RemoveListener(nsITransactionListener* aListener)
|
||||
{
|
||||
NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
|
||||
return mListeners.RemoveObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
|
||||
if (NS_WARN_IF(!aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return RemoveTransactionListener(*aListener) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::ClearUndoStack()
|
||||
TransactionManager::ClearUndoStack()
|
||||
{
|
||||
if (NS_WARN_IF(!mDoStack.IsEmpty())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mUndoStack.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::ClearRedoStack()
|
||||
TransactionManager::ClearRedoStack()
|
||||
{
|
||||
if (NS_WARN_IF(!mDoStack.IsEmpty())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mRedoStack.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, bool *aInterrupt)
|
||||
TransactionManager::WillDoNotify(nsITransaction* aTransaction,
|
||||
bool* aInterrupt)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -450,7 +487,8 @@ nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, bool *aInterrup
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResult)
|
||||
TransactionManager::DidDoNotify(nsITransaction* aTransaction,
|
||||
nsresult aDoResult)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -465,7 +503,8 @@ nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResu
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, bool *aInterrupt)
|
||||
TransactionManager::WillUndoNotify(nsITransaction* aTransaction,
|
||||
bool* aInterrupt)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -480,7 +519,8 @@ nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, bool *aInterr
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult)
|
||||
TransactionManager::DidUndoNotify(nsITransaction* aTransaction,
|
||||
nsresult aUndoResult)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -495,7 +535,8 @@ nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndo
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, bool *aInterrupt)
|
||||
TransactionManager::WillRedoNotify(nsITransaction* aTransaction,
|
||||
bool* aInterrupt)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -510,7 +551,8 @@ nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, bool *aInterr
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult)
|
||||
TransactionManager::DidRedoNotify(nsITransaction* aTransaction,
|
||||
nsresult aRedoResult)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -525,7 +567,7 @@ nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedo
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillBeginBatchNotify(bool *aInterrupt)
|
||||
TransactionManager::WillBeginBatchNotify(bool* aInterrupt)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -540,7 +582,7 @@ nsTransactionManager::WillBeginBatchNotify(bool *aInterrupt)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
|
||||
TransactionManager::DidBeginBatchNotify(nsresult aResult)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -555,7 +597,7 @@ nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillEndBatchNotify(bool *aInterrupt)
|
||||
TransactionManager::WillEndBatchNotify(bool* aInterrupt)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -570,7 +612,7 @@ nsTransactionManager::WillEndBatchNotify(bool *aInterrupt)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidEndBatchNotify(nsresult aResult)
|
||||
TransactionManager::DidEndBatchNotify(nsresult aResult)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -585,7 +627,9 @@ nsTransactionManager::DidEndBatchNotify(nsresult aResult)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTransaction, bool *aInterrupt)
|
||||
TransactionManager::WillMergeNotify(nsITransaction* aTop,
|
||||
nsITransaction* aTransaction,
|
||||
bool* aInterrupt)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -600,10 +644,10 @@ nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTra
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
|
||||
nsITransaction *aTransaction,
|
||||
bool aDidMerge,
|
||||
nsresult aMergeResult)
|
||||
TransactionManager::DidMergeNotify(nsITransaction* aTop,
|
||||
nsITransaction* aTransaction,
|
||||
bool aDidMerge,
|
||||
nsresult aMergeResult)
|
||||
{
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
@ -619,45 +663,42 @@ nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::BeginTransaction(nsITransaction *aTransaction,
|
||||
nsISupports *aData)
|
||||
TransactionManager::BeginTransaction(nsITransaction* aTransaction,
|
||||
nsISupports* aData)
|
||||
{
|
||||
// XXX: POSSIBLE OPTIMIZATION
|
||||
// We could use a factory that pre-allocates/recycles transaction items.
|
||||
RefPtr<nsTransactionItem> tx = new nsTransactionItem(aTransaction);
|
||||
if (!tx) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
RefPtr<TransactionItem> transactionItem = new TransactionItem(aTransaction);
|
||||
|
||||
if (aData) {
|
||||
nsCOMArray<nsISupports>& data = tx->GetData();
|
||||
nsCOMArray<nsISupports>& data = transactionItem->GetData();
|
||||
data.AppendObject(aData);
|
||||
}
|
||||
|
||||
mDoStack.Push(tx);
|
||||
mDoStack.Push(transactionItem);
|
||||
|
||||
nsresult rv = tx->DoTransaction();
|
||||
nsresult rv = transactionItem->DoTransaction();
|
||||
if (NS_FAILED(rv)) {
|
||||
tx = mDoStack.Pop();
|
||||
transactionItem = mDoStack.Pop();
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
||||
TransactionManager::EndTransaction(bool aAllowEmpty)
|
||||
{
|
||||
RefPtr<nsTransactionItem> tx = mDoStack.Pop();
|
||||
if (!tx) {
|
||||
RefPtr<TransactionItem> transactionItem = mDoStack.Pop();
|
||||
if (!transactionItem) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransaction> tint = tx->GetTransaction();
|
||||
if (!tint && !aAllowEmpty) {
|
||||
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
|
||||
if (!transaction && !aAllowEmpty) {
|
||||
// If we get here, the transaction must be a dummy batch transaction
|
||||
// created by BeginBatch(). If it contains no children, get rid of it!
|
||||
int32_t nc = 0;
|
||||
tx->GetNumberOfChildren(&nc);
|
||||
transactionItem->GetNumberOfChildren(&nc);
|
||||
if (!nc) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -666,10 +707,7 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
||||
// Check if the transaction is transient. If it is, there's nothing
|
||||
// more to do, just return.
|
||||
bool isTransient = false;
|
||||
nsresult rv = NS_OK;
|
||||
if (tint) {
|
||||
rv = tint->GetIsTransient(&isTransient);
|
||||
}
|
||||
nsresult rv = transaction ? transaction->GetIsTransient(&isTransient) : NS_OK;
|
||||
if (NS_FAILED(rv) || isTransient || !mMaxTransactionCount) {
|
||||
// XXX: Should we be clearing the redo stack if the transaction
|
||||
// is transient and there is nothing on the do stack?
|
||||
@ -679,31 +717,31 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
||||
// Check if there is a transaction on the do stack. If there is,
|
||||
// the current transaction is a "sub" transaction, and should
|
||||
// be added to the transaction at the top of the do stack.
|
||||
RefPtr<nsTransactionItem> top = mDoStack.Peek();
|
||||
if (top) {
|
||||
return top->AddChild(tx); // XXX: What do we do if this fails?
|
||||
RefPtr<TransactionItem> topTransactionItem = mDoStack.Peek();
|
||||
if (topTransactionItem) {
|
||||
// XXX: What do we do if this fails?
|
||||
return topTransactionItem->AddChild(transactionItem);
|
||||
}
|
||||
|
||||
// The transaction succeeded, so clear the redo stack.
|
||||
rv = ClearRedoStack();
|
||||
if (NS_FAILED(rv)) {
|
||||
// XXX: What do we do if this fails?
|
||||
}
|
||||
mRedoStack.Clear();
|
||||
|
||||
// Check if we can coalesce this transaction with the one at the top
|
||||
// of the undo stack.
|
||||
top = mUndoStack.Peek();
|
||||
if (tint && top) {
|
||||
topTransactionItem = mUndoStack.Peek();
|
||||
if (transaction && topTransactionItem) {
|
||||
bool didMerge = false;
|
||||
nsCOMPtr<nsITransaction> topTransaction = top->GetTransaction();
|
||||
nsCOMPtr<nsITransaction> topTransaction =
|
||||
topTransactionItem->GetTransaction();
|
||||
if (topTransaction) {
|
||||
bool doInterrupt = false;
|
||||
rv = WillMergeNotify(topTransaction, tint, &doInterrupt);
|
||||
rv = WillMergeNotify(topTransaction, transaction, &doInterrupt);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!doInterrupt) {
|
||||
rv = topTransaction->Merge(tint, &didMerge);
|
||||
nsresult rv2 = DidMergeNotify(topTransaction, tint, didMerge, rv);
|
||||
rv = topTransaction->Merge(transaction, &didMerge);
|
||||
nsresult rv2 =
|
||||
DidMergeNotify(topTransaction, transaction, didMerge, rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
@ -721,10 +759,12 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
||||
// pop the bottom transaction off the undo stack and release it!
|
||||
int32_t sz = mUndoStack.GetSize();
|
||||
if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
|
||||
RefPtr<nsTransactionItem> overflow = mUndoStack.PopBottom();
|
||||
RefPtr<TransactionItem> overflow = mUndoStack.PopBottom();
|
||||
}
|
||||
|
||||
// Push the transaction on the undo stack:
|
||||
mUndoStack.Push(tx.forget());
|
||||
mUndoStack.Push(transactionItem.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
116
editor/txmgr/TransactionManager.h
Normal file
116
editor/txmgr/TransactionManager.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_TransactionManager_h
|
||||
#define mozilla_TransactionManager_h
|
||||
|
||||
#include "mozilla/TransactionStack.h"
|
||||
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsITransactionManager.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nscore.h"
|
||||
|
||||
class nsITransaction;
|
||||
class nsITransactionListener;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class TransactionManager final : public nsITransactionManager
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
explicit TransactionManager(int32_t aMaxTransactionCount = -1);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TransactionManager,
|
||||
nsITransactionManager)
|
||||
|
||||
NS_DECL_NSITRANSACTIONMANAGER
|
||||
|
||||
already_AddRefed<nsITransaction> PeekUndoStack();
|
||||
already_AddRefed<nsITransaction> PeekRedoStack();
|
||||
|
||||
nsresult Undo();
|
||||
nsresult Redo();
|
||||
|
||||
size_t NumberOfUndoItems() const
|
||||
{
|
||||
return mUndoStack.GetSize();
|
||||
}
|
||||
size_t NumberOfRedoItems() const
|
||||
{
|
||||
return mRedoStack.GetSize();
|
||||
}
|
||||
|
||||
bool EnableUndoRedo(int32_t aMaxTransactionCount = -1);
|
||||
bool DisableUndoRedo()
|
||||
{
|
||||
return EnableUndoRedo(0);
|
||||
}
|
||||
bool ClearUndoRedo()
|
||||
{
|
||||
if (NS_WARN_IF(!mDoStack.IsEmpty())) {
|
||||
return false;
|
||||
}
|
||||
mUndoStack.Clear();
|
||||
mRedoStack.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddTransactionListener(nsITransactionListener& aListener)
|
||||
{
|
||||
// XXX Shouldn't we check if aListener has already been in mListeners?
|
||||
return mListeners.AppendObject(&aListener);
|
||||
}
|
||||
bool RemoveTransactionListener(nsITransactionListener& aListener)
|
||||
{
|
||||
return mListeners.RemoveObject(&aListener);
|
||||
}
|
||||
|
||||
nsresult WillDoNotify(nsITransaction* aTransaction, bool* aInterrupt);
|
||||
nsresult DidDoNotify(nsITransaction* aTransaction, nsresult aExecuteResult);
|
||||
nsresult WillUndoNotify(nsITransaction* aTransaction, bool* aInterrupt);
|
||||
nsresult DidUndoNotify(nsITransaction* aTransaction, nsresult aUndoResult);
|
||||
nsresult WillRedoNotify(nsITransaction* aTransaction, bool *aInterrupt);
|
||||
nsresult DidRedoNotify(nsITransaction* aTransaction, nsresult aRedoResult);
|
||||
nsresult WillBeginBatchNotify(bool* aInterrupt);
|
||||
nsresult DidBeginBatchNotify(nsresult aResult);
|
||||
nsresult WillEndBatchNotify(bool* aInterrupt);
|
||||
nsresult DidEndBatchNotify(nsresult aResult);
|
||||
nsresult WillMergeNotify(nsITransaction* aTop,
|
||||
nsITransaction* aTransaction,
|
||||
bool* aInterrupt);
|
||||
nsresult DidMergeNotify(nsITransaction* aTop,
|
||||
nsITransaction* aTransaction,
|
||||
bool aDidMerge,
|
||||
nsresult aMergeResult);
|
||||
|
||||
private:
|
||||
virtual ~TransactionManager() = default;
|
||||
|
||||
nsresult BeginTransaction(nsITransaction* aTransaction,
|
||||
nsISupports* aData);
|
||||
nsresult EndTransaction(bool aAllowEmpty);
|
||||
|
||||
int32_t mMaxTransactionCount;
|
||||
TransactionStack mDoStack;
|
||||
TransactionStack mUndoStack;
|
||||
TransactionStack mRedoStack;
|
||||
nsCOMArray<nsITransactionListener> mListeners;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
mozilla::TransactionManager*
|
||||
nsITransactionManager::AsTransactionManager()
|
||||
{
|
||||
return static_cast<mozilla::TransactionManager*>(this);
|
||||
}
|
||||
|
||||
#endif // #ifndef mozilla_TransactionManager_h
|
@ -7,32 +7,35 @@
|
||||
|
||||
#include "mozilla/Module.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/TransactionManager.h"
|
||||
#include "nsID.h"
|
||||
#include "nsITransactionManager.h"
|
||||
#include "nsTransactionManager.h"
|
||||
#include "nsTransactionManagerCID.h"
|
||||
|
||||
using mozilla::TransactionManager;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Define the contructor function for the objects
|
||||
//
|
||||
// NOTE: This creates an instance of objects by using the default constructor
|
||||
//
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransactionManager)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TransactionManager)
|
||||
NS_DEFINE_NAMED_CID(NS_TRANSACTIONMANAGER_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kTxMgrCIDs[] = {
|
||||
{ &kNS_TRANSACTIONMANAGER_CID, false, nullptr, nsTransactionManagerConstructor },
|
||||
{ nullptr }
|
||||
{ &kNS_TRANSACTIONMANAGER_CID, false, nullptr,
|
||||
TransactionManagerConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kTxMgrContracts[] = {
|
||||
{ NS_TRANSACTIONMANAGER_CONTRACTID, &kNS_TRANSACTIONMANAGER_CID },
|
||||
{ nullptr }
|
||||
{ NS_TRANSACTIONMANAGER_CONTRACTID, &kNS_TRANSACTIONMANAGER_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module kTxMgrModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kTxMgrCIDs,
|
||||
kTxMgrContracts
|
||||
mozilla::Module::kVersion,
|
||||
kTxMgrCIDs,
|
||||
kTxMgrContracts
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(nsTransactionManagerModule) = &kTxMgrModule;
|
116
editor/txmgr/TransactionStack.cpp
Normal file
116
editor/txmgr/TransactionStack.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/TransactionStack.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nscore.h"
|
||||
#include "TransactionItem.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class TransactionStackDeallocator final : public nsDequeFunctor
|
||||
{
|
||||
virtual void operator()(void* aObject) override
|
||||
{
|
||||
RefPtr<TransactionItem> releaseMe =
|
||||
dont_AddRef(static_cast<TransactionItem*>(aObject));
|
||||
}
|
||||
};
|
||||
|
||||
TransactionStack::TransactionStack(Type aType)
|
||||
: nsDeque(new TransactionStackDeallocator())
|
||||
, mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
TransactionStack::~TransactionStack()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void
|
||||
TransactionStack::Push(TransactionItem* aTransactionItem)
|
||||
{
|
||||
if (!aTransactionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<TransactionItem> item(aTransactionItem);
|
||||
Push(item.forget());
|
||||
}
|
||||
|
||||
void
|
||||
TransactionStack::Push(already_AddRefed<TransactionItem> aTransactionItem)
|
||||
{
|
||||
RefPtr<TransactionItem> item(aTransactionItem);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDeque::Push(item.forget().take());
|
||||
}
|
||||
|
||||
already_AddRefed<TransactionItem>
|
||||
TransactionStack::Pop()
|
||||
{
|
||||
RefPtr<TransactionItem> item =
|
||||
dont_AddRef(static_cast<TransactionItem*>(nsDeque::Pop()));
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<TransactionItem>
|
||||
TransactionStack::PopBottom()
|
||||
{
|
||||
RefPtr<TransactionItem> item =
|
||||
dont_AddRef(static_cast<TransactionItem*>(nsDeque::PopFront()));
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<TransactionItem>
|
||||
TransactionStack::Peek()
|
||||
{
|
||||
RefPtr<TransactionItem> item =
|
||||
static_cast<TransactionItem*>(nsDeque::Peek());
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<TransactionItem>
|
||||
TransactionStack::GetItem(int32_t aIndex)
|
||||
{
|
||||
if (aIndex < 0 || aIndex >= static_cast<int32_t>(nsDeque::GetSize())) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<TransactionItem> item =
|
||||
static_cast<TransactionItem*>(nsDeque::ObjectAt(aIndex));
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
void
|
||||
TransactionStack::Clear()
|
||||
{
|
||||
while (GetSize() != 0) {
|
||||
RefPtr<TransactionItem> item =
|
||||
mType == FOR_UNDO ? Pop() : PopBottom();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TransactionStack::DoTraverse(nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
size_t size = GetSize();
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
TransactionItem* item = static_cast<TransactionItem*>(nsDeque::ObjectAt(i));
|
||||
if (item) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mDeque[i]");
|
||||
cb.NoteNativeChild(item,
|
||||
NS_CYCLE_COLLECTION_PARTICIPANT(TransactionItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -3,28 +3,31 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsTransactionStack_h__
|
||||
#define nsTransactionStack_h__
|
||||
#ifndef mozilla_TransactionStack_h
|
||||
#define mozilla_TransactionStack_h
|
||||
|
||||
#include "nsDeque.h"
|
||||
|
||||
class nsCycleCollectionTraversalCallback;
|
||||
class nsTransactionItem;
|
||||
|
||||
class nsTransactionStack : private nsDeque
|
||||
namespace mozilla {
|
||||
|
||||
class TransactionItem;
|
||||
|
||||
class TransactionStack : private nsDeque
|
||||
{
|
||||
public:
|
||||
enum Type { FOR_UNDO, FOR_REDO };
|
||||
|
||||
explicit nsTransactionStack(Type aType);
|
||||
~nsTransactionStack();
|
||||
explicit TransactionStack(Type aType);
|
||||
~TransactionStack();
|
||||
|
||||
void Push(nsTransactionItem *aTransactionItem);
|
||||
void Push(already_AddRefed<nsTransactionItem> aTransactionItem);
|
||||
already_AddRefed<nsTransactionItem> Pop();
|
||||
already_AddRefed<nsTransactionItem> PopBottom();
|
||||
already_AddRefed<nsTransactionItem> Peek();
|
||||
already_AddRefed<nsTransactionItem> GetItem(int32_t aIndex);
|
||||
void Push(TransactionItem* aTransactionItem);
|
||||
void Push(already_AddRefed<TransactionItem> aTransactionItem);
|
||||
already_AddRefed<TransactionItem> Pop();
|
||||
already_AddRefed<TransactionItem> PopBottom();
|
||||
already_AddRefed<TransactionItem> Peek();
|
||||
already_AddRefed<TransactionItem> GetItem(int32_t aIndex);
|
||||
void Clear();
|
||||
int32_t GetSize() const { return static_cast<int32_t>(nsDeque::GetSize()); }
|
||||
bool IsEmpty() const { return GetSize() == 0; }
|
||||
@ -36,4 +39,6 @@ private:
|
||||
const Type mType;
|
||||
};
|
||||
|
||||
#endif // nsTransactionStack_h__
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_TransactionStack_h
|
@ -18,11 +18,16 @@ EXPORTS += [
|
||||
'nsTransactionManagerCID.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'TransactionManager.h',
|
||||
'TransactionStack.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsTransactionItem.cpp',
|
||||
'nsTransactionManager.cpp',
|
||||
'nsTransactionManagerFactory.cpp',
|
||||
'nsTransactionStack.cpp',
|
||||
'TransactionItem.cpp',
|
||||
'TransactionManager.cpp',
|
||||
'TransactionManagerFactory.cpp',
|
||||
'TransactionStack.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -7,11 +7,11 @@
|
||||
#include "nsITransaction.idl"
|
||||
#include "nsITransactionListener.idl"
|
||||
|
||||
%{ C++
|
||||
|
||||
#define NS_TRANSACTIONMANAGER_CONTRACTID "@mozilla.org/transactionmanager;1"
|
||||
|
||||
%} C++
|
||||
%{C++
|
||||
namespace mozilla {
|
||||
class TransactionManager;
|
||||
} // namespace mozilla
|
||||
%}
|
||||
|
||||
/**
|
||||
* The nsITransactionManager interface.
|
||||
@ -149,5 +149,25 @@ interface nsITransactionManager : nsISupports
|
||||
* @param aListener the lister to remove.
|
||||
*/
|
||||
void RemoveListener(in nsITransactionListener aListener);
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* AsTransactionManager() returns a pointer to TransactionManager class.
|
||||
*
|
||||
* In order to avoid circular dependency issues, this method is defined
|
||||
* in mozilla/TransactionManager.h. Consumers need to #include that header.
|
||||
*/
|
||||
inline mozilla::TransactionManager* AsTransactionManager();
|
||||
%}
|
||||
};
|
||||
|
||||
%{ C++
|
||||
|
||||
#define NS_TRANSACTIONMANAGER_CONTRACTID "@mozilla.org/transactionmanager;1"
|
||||
|
||||
// 9C8F9601-801A-11d2-98BA-00805F297D89
|
||||
#define NS_TRANSACTIONMANAGER_CID \
|
||||
{ 0x9c8f9601, 0x801a, 0x11d2, \
|
||||
{ 0x98, 0xba, 0x0, 0x80, 0x5f, 0x29, 0x7d, 0x89 } }
|
||||
|
||||
%} C++
|
||||
|
@ -1,68 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsTransactionItem_h__
|
||||
#define nsTransactionItem_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nscore.h"
|
||||
|
||||
class nsITransaction;
|
||||
class nsTransactionManager;
|
||||
class nsTransactionStack;
|
||||
|
||||
class nsTransactionItem final
|
||||
{
|
||||
nsCOMArray<nsISupports> mData;
|
||||
nsCOMPtr<nsITransaction> mTransaction;
|
||||
nsTransactionStack *mUndoStack;
|
||||
nsTransactionStack *mRedoStack;
|
||||
|
||||
public:
|
||||
|
||||
explicit nsTransactionItem(nsITransaction *aTransaction);
|
||||
NS_METHOD_(MozExternalRefCountType) AddRef();
|
||||
NS_METHOD_(MozExternalRefCountType) Release();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsTransactionItem)
|
||||
|
||||
virtual nsresult AddChild(nsTransactionItem *aTransactionItem);
|
||||
already_AddRefed<nsITransaction> GetTransaction();
|
||||
virtual nsresult GetIsBatch(bool *aIsBatch);
|
||||
virtual nsresult GetNumberOfChildren(int32_t *aNumChildren);
|
||||
virtual nsresult GetChild(int32_t aIndex, nsTransactionItem **aChild);
|
||||
|
||||
virtual nsresult DoTransaction(void);
|
||||
virtual nsresult UndoTransaction(nsTransactionManager *aTxMgr);
|
||||
virtual nsresult RedoTransaction(nsTransactionManager *aTxMgr);
|
||||
|
||||
nsCOMArray<nsISupports>& GetData()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual nsresult UndoChildren(nsTransactionManager *aTxMgr);
|
||||
virtual nsresult RedoChildren(nsTransactionManager *aTxMgr);
|
||||
|
||||
virtual nsresult RecoverFromUndoError(nsTransactionManager *aTxMgr);
|
||||
virtual nsresult RecoverFromRedoError(nsTransactionManager *aTxMgr);
|
||||
|
||||
virtual nsresult GetNumberOfUndoItems(int32_t *aNumItems);
|
||||
virtual nsresult GetNumberOfRedoItems(int32_t *aNumItems);
|
||||
|
||||
void CleanUp();
|
||||
protected:
|
||||
virtual ~nsTransactionItem();
|
||||
|
||||
nsCycleCollectingAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
};
|
||||
|
||||
#endif // nsTransactionItem_h__
|
@ -1,83 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsTransactionManager_h__
|
||||
#define nsTransactionManager_h__
|
||||
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsTransactionStack.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsITransactionManager.h"
|
||||
#include "nsTransactionStack.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nscore.h"
|
||||
|
||||
class nsITransaction;
|
||||
class nsITransactionListener;
|
||||
|
||||
/** implementation of a transaction manager object.
|
||||
*
|
||||
*/
|
||||
class nsTransactionManager final : public nsITransactionManager
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
private:
|
||||
|
||||
int32_t mMaxTransactionCount;
|
||||
nsTransactionStack mDoStack;
|
||||
nsTransactionStack mUndoStack;
|
||||
nsTransactionStack mRedoStack;
|
||||
nsCOMArray<nsITransactionListener> mListeners;
|
||||
|
||||
/** The default destructor.
|
||||
*/
|
||||
virtual ~nsTransactionManager();
|
||||
|
||||
public:
|
||||
|
||||
/** The default constructor.
|
||||
*/
|
||||
explicit nsTransactionManager(int32_t aMaxTransactionCount=-1);
|
||||
|
||||
/* Macro for AddRef(), Release(), and QueryInterface() */
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTransactionManager,
|
||||
nsITransactionManager)
|
||||
|
||||
/* nsITransactionManager method implementations. */
|
||||
NS_DECL_NSITRANSACTIONMANAGER
|
||||
|
||||
already_AddRefed<nsITransaction> PeekUndoStack();
|
||||
already_AddRefed<nsITransaction> PeekRedoStack();
|
||||
|
||||
virtual nsresult WillDoNotify(nsITransaction *aTransaction, bool *aInterrupt);
|
||||
virtual nsresult DidDoNotify(nsITransaction *aTransaction, nsresult aExecuteResult);
|
||||
virtual nsresult WillUndoNotify(nsITransaction *aTransaction, bool *aInterrupt);
|
||||
virtual nsresult DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult);
|
||||
virtual nsresult WillRedoNotify(nsITransaction *aTransaction, bool *aInterrupt);
|
||||
virtual nsresult DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult);
|
||||
virtual nsresult WillBeginBatchNotify(bool *aInterrupt);
|
||||
virtual nsresult DidBeginBatchNotify(nsresult aResult);
|
||||
virtual nsresult WillEndBatchNotify(bool *aInterrupt);
|
||||
virtual nsresult DidEndBatchNotify(nsresult aResult);
|
||||
virtual nsresult WillMergeNotify(nsITransaction *aTop,
|
||||
nsITransaction *aTransaction,
|
||||
bool *aInterrupt);
|
||||
virtual nsresult DidMergeNotify(nsITransaction *aTop,
|
||||
nsITransaction *aTransaction,
|
||||
bool aDidMerge,
|
||||
nsresult aMergeResult);
|
||||
|
||||
private:
|
||||
|
||||
/* nsTransactionManager specific private methods. */
|
||||
virtual nsresult BeginTransaction(nsITransaction *aTransaction,
|
||||
nsISupports *aData);
|
||||
virtual nsresult EndTransaction(bool aAllowEmpty);
|
||||
};
|
||||
|
||||
#endif // nsTransactionManager_h__
|
@ -3,12 +3,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsTransactionManagerCID_h__
|
||||
#define nsTransactionManagerCID_h__
|
||||
// XXX Needs to modify mailnews/base/src/nsMsgWindow.cpp before removing this
|
||||
// header file.
|
||||
#include "nsITransactionManager.h"
|
||||
|
||||
#define NS_TRANSACTIONMANAGER_CID \
|
||||
{ /* 9C8F9601-801A-11d2-98BA-00805F297D89 */ \
|
||||
0x9c8f9601, 0x801a, 0x11d2, \
|
||||
{ 0x98, 0xba, 0x0, 0x80, 0x5f, 0x29, 0x7d, 0x89 } }
|
||||
|
||||
#endif /* nsTransactionManagerCID_h__ */
|
||||
|
@ -1,108 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nsTransactionItem.h"
|
||||
#include "nsTransactionStack.h"
|
||||
#include "nscore.h"
|
||||
|
||||
class nsTransactionStackDeallocator : public nsDequeFunctor {
|
||||
virtual void operator()(void* aObject) override
|
||||
{
|
||||
RefPtr<nsTransactionItem> releaseMe = dont_AddRef(static_cast<nsTransactionItem*>(aObject));
|
||||
}
|
||||
};
|
||||
|
||||
nsTransactionStack::nsTransactionStack(Type aType)
|
||||
: nsDeque(new nsTransactionStackDeallocator())
|
||||
, mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
nsTransactionStack::~nsTransactionStack()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void
|
||||
nsTransactionStack::Push(nsTransactionItem* aTransactionItem)
|
||||
{
|
||||
if (!aTransactionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<nsTransactionItem> item(aTransactionItem);
|
||||
Push(item.forget());
|
||||
}
|
||||
|
||||
void
|
||||
nsTransactionStack::Push(already_AddRefed<nsTransactionItem> aTransactionItem)
|
||||
{
|
||||
RefPtr<nsTransactionItem> item(aTransactionItem);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDeque::Push(item.forget().take());
|
||||
}
|
||||
|
||||
already_AddRefed<nsTransactionItem>
|
||||
nsTransactionStack::Pop()
|
||||
{
|
||||
RefPtr<nsTransactionItem> item =
|
||||
dont_AddRef(static_cast<nsTransactionItem*>(nsDeque::Pop()));
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTransactionItem>
|
||||
nsTransactionStack::PopBottom()
|
||||
{
|
||||
RefPtr<nsTransactionItem> item =
|
||||
dont_AddRef(static_cast<nsTransactionItem*>(nsDeque::PopFront()));
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTransactionItem>
|
||||
nsTransactionStack::Peek()
|
||||
{
|
||||
RefPtr<nsTransactionItem> item =
|
||||
static_cast<nsTransactionItem*>(nsDeque::Peek());
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTransactionItem>
|
||||
nsTransactionStack::GetItem(int32_t aIndex)
|
||||
{
|
||||
if (aIndex < 0 || aIndex >= static_cast<int32_t>(nsDeque::GetSize())) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<nsTransactionItem> item =
|
||||
static_cast<nsTransactionItem*>(nsDeque::ObjectAt(aIndex));
|
||||
return item.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsTransactionStack::Clear()
|
||||
{
|
||||
while (GetSize() != 0) {
|
||||
RefPtr<nsTransactionItem> item =
|
||||
mType == FOR_UNDO ? Pop() : PopBottom();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTransactionStack::DoTraverse(nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
int32_t size = GetSize();
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
nsTransactionItem* item = static_cast<nsTransactionItem*>(nsDeque::ObjectAt(i));
|
||||
if (item) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mDeque[i]");
|
||||
cb.NoteNativeChild(item, NS_CYCLE_COLLECTION_PARTICIPANT(nsTransactionItem));
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user