Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Bogdan Tara 2018-03-27 12:16:26 +03:00
commit 39d9b048b9
185 changed files with 2924 additions and 2089 deletions

View File

@ -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

View File

@ -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);

View File

@ -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() {

View File

@ -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;"

View File

@ -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

View File

@ -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);

View File

@ -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]

View File

@ -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);
});

View 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>

View File

@ -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);

View File

@ -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) {

View File

@ -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");
}
});
}
}
};
/*

View File

@ -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"
}
}
}
}
}

View File

@ -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]

View File

@ -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");
});

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 =

View File

@ -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>

View File

@ -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>

View File

@ -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()"

View File

@ -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();"

View File

@ -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()"

View File

@ -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);

View File

@ -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"

View File

@ -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;"

View File

@ -263,6 +263,7 @@ let gSiteDataSettings = {
onClickTreeCol(e) {
this._sortSites(this._sites, e.target);
this._buildSitesList(this._sites);
this._list.clearSelection();
},
onCommandSearch() {

View File

@ -3426,7 +3426,8 @@ var SessionStoreInternal = {
skipAnimation: true,
noInitialLabel: true,
userContextId,
skipBackgroundNotify: true });
skipBackgroundNotify: true,
bulkOrderedOpen: this._browserSetState });
if (select) {
let leftoverTab = tabbrowser.selectedTab;

View File

@ -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, "");

View File

@ -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"],
];

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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");

View File

@ -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");
/**

View File

@ -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;

View File

@ -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]"

View File

@ -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) {

View File

@ -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);

View File

@ -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, {

View File

@ -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, {

View File

@ -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, {

View File

@ -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, {

View File

@ -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.");

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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.");

View File

@ -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,

View File

@ -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.");

View File

@ -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);

View File

@ -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;
});

View File

@ -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();

View File

@ -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.`);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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 });
},

View File

@ -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) {

View File

@ -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 });
},

View File

@ -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();

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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]

View File

@ -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);

View File

@ -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.");

View File

@ -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.");

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -240,7 +240,6 @@ LOCAL_INCLUDES += [
'/dom/security',
'/dom/xbl',
'/dom/xul',
'/editor/txmgr',
'/image',
'/layout/forms',
'/layout/generic',

View File

@ -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) {

View File

@ -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>

View File

@ -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"

View File

@ -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;
}

View File

@ -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.

View File

@ -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();

View File

@ -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.

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -77,7 +77,6 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
'/dom/base',
'/editor/txmgr',
'/extensions/spellcheck/src',
'/layout/generic',
'/layout/style',

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View 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

View File

@ -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;

View 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

View File

@ -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

View File

@ -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'

View File

@ -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++

View File

@ -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__

View File

@ -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__

View File

@ -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__ */

View File

@ -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