Merge m-c to inbound. a=merge
This commit is contained in:
@@ -1498,9 +1498,6 @@ var gBrowserInit = {
|
||||
}
|
||||
}
|
||||
|
||||
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
|
||||
setTimeout(function() { SafeBrowsing.init(); }, 2000);
|
||||
|
||||
Services.obs.addObserver(gIdentityHandler, "perm-changed");
|
||||
Services.obs.addObserver(gRemoteControl, "remote-active");
|
||||
Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history");
|
||||
@@ -4991,6 +4988,16 @@ var CombinedStopReload = {
|
||||
});
|
||||
},
|
||||
|
||||
/* This function is necessary to correctly vertically center the animation
|
||||
within the toolbar, which uses -moz-pack-align:stretch; and thus a height
|
||||
which is dependant on the font-size. */
|
||||
setAnimationImageHeightRelativeToToolbarButtonHeight() {
|
||||
let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let toolbarItem = this.stopReloadContainer.closest(".customization-target > toolbaritem");
|
||||
let bounds = dwu.getBoundsWithoutFlushing(toolbarItem);
|
||||
toolbarItem.style.setProperty("--toolbarbutton-height", bounds.height + "px");
|
||||
},
|
||||
|
||||
switchToStop(aRequest, aWebProgress) {
|
||||
if (!this._initialized || !this._shouldSwitch(aRequest))
|
||||
return;
|
||||
@@ -5002,10 +5009,12 @@ var CombinedStopReload = {
|
||||
this.animate;
|
||||
|
||||
this._cancelTransition();
|
||||
if (shouldAnimate)
|
||||
if (shouldAnimate) {
|
||||
this.setAnimationImageHeightRelativeToToolbarButtonHeight();
|
||||
this.stopReloadContainer.setAttribute("animate", "true");
|
||||
else
|
||||
} else {
|
||||
this.stopReloadContainer.removeAttribute("animate");
|
||||
}
|
||||
this.reload.setAttribute("displaystop", "true");
|
||||
},
|
||||
|
||||
@@ -5020,10 +5029,12 @@ var CombinedStopReload = {
|
||||
!aWebProgress.isLoadingDocument &&
|
||||
this.animate;
|
||||
|
||||
if (shouldAnimate)
|
||||
if (shouldAnimate) {
|
||||
this.setAnimationImageHeightRelativeToToolbarButtonHeight();
|
||||
this.stopReloadContainer.setAttribute("animate", "true");
|
||||
else
|
||||
} else {
|
||||
this.stopReloadContainer.removeAttribute("animate");
|
||||
}
|
||||
|
||||
this.reload.removeAttribute("displaystop");
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ add_task(async function() {
|
||||
await pushPrefs(["accessibility.tabfocus", 7]);
|
||||
|
||||
// When the onboarding component is enabled, it would inject extra tour notification into
|
||||
// the newtab page so there would be 2 more notification close button and action button
|
||||
// the newtab page so there would be 3 more overlay button, notification close button and action button
|
||||
let onbardingEnabled = AppConstants.NIGHTLY_BUILD && Services.prefs.getBoolPref("browser.onboarding.enabled");
|
||||
|
||||
// Focus count in new tab page.
|
||||
@@ -26,7 +26,7 @@ add_task(async function() {
|
||||
}
|
||||
let tab = await addNewTabPageTab();
|
||||
if (onbardingEnabled) {
|
||||
FOCUS_COUNT += 2;
|
||||
FOCUS_COUNT += 3;
|
||||
await promiseTourNotificationOpened(tab.linkedBrowser);
|
||||
}
|
||||
gURLBar.focus();
|
||||
@@ -37,7 +37,7 @@ add_task(async function() {
|
||||
|
||||
let expectedCount = 4;
|
||||
if (onbardingEnabled) {
|
||||
expectedCount += 2;
|
||||
expectedCount += 3;
|
||||
}
|
||||
countFocus(expectedCount);
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ XPCOMUtils.defineLazyGetter(this, "WeaveService", () =>
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
|
||||
"resource://gre/modules/ContextualIdentityService.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
|
||||
"resource://gre/modules/SafeBrowsing.jsm");
|
||||
|
||||
// lazy module getters
|
||||
|
||||
/* global AboutHome:false, AboutNewTab:false, AddonManager:false, AppMenuNotifications:false,
|
||||
@@ -1185,6 +1188,10 @@ BrowserGlue.prototype = {
|
||||
ContextualIdentityService.load();
|
||||
});
|
||||
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
SafeBrowsing.init();
|
||||
}, 5000);
|
||||
|
||||
this._sanitizer.onStartup();
|
||||
E10SAccessibilityCheck.onWindowsRestored();
|
||||
},
|
||||
|
||||
@@ -3279,8 +3279,8 @@ var SessionStoreInternal = {
|
||||
}
|
||||
}
|
||||
|
||||
let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") &&
|
||||
this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
|
||||
let restoreOnDemand = this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
|
||||
let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") && restoreOnDemand;
|
||||
|
||||
for (var t = 0; t < newTabCount; t++) {
|
||||
let tabData = winData.tabs[t];
|
||||
@@ -3333,6 +3333,13 @@ var SessionStoreInternal = {
|
||||
tabbrowser.selectedTab = tab;
|
||||
tabbrowser.removeTab(leftoverTab);
|
||||
}
|
||||
|
||||
// Prepare connection to the host when users hover mouse over this
|
||||
// tab. If we're not restoring on demand, we'll prepare connection
|
||||
// when we're restoring next tab.
|
||||
if (!tabData.pinned && restoreOnDemand) {
|
||||
this.speculativeConnectOnTabHover(tab, url);
|
||||
}
|
||||
}
|
||||
|
||||
tabs.push(tab);
|
||||
@@ -3417,6 +3424,42 @@ var SessionStoreInternal = {
|
||||
this._sendRestoreCompletedNotifications();
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare connection to host beforehand.
|
||||
*
|
||||
* @param url
|
||||
* URL of a host.
|
||||
* @returns a flag indicates whether a connection has been made
|
||||
*/
|
||||
prepareConnectionToHost(url) {
|
||||
if (!url.startsWith("about:")) {
|
||||
let sc = Services.io.QueryInterface(Ci.nsISpeculativeConnect);
|
||||
let uri = Services.io.newURI(url);
|
||||
sc.speculativeConnect(uri, null, null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Make a connection to a host when users hover mouse on a tab.
|
||||
*
|
||||
* @param tab
|
||||
* A tab to set up a hover listener.
|
||||
* @param url
|
||||
* URL of a host.
|
||||
*/
|
||||
speculativeConnectOnTabHover(tab, url) {
|
||||
tab.addEventListener("mouseover", () => {
|
||||
let prepared = this.prepareConnectionToHost(url);
|
||||
// This is used to test if a connection has been made beforehand.
|
||||
if (gDebuggingEnabled) {
|
||||
tab.__test_connection_prepared = prepared;
|
||||
tab.__test_connection_url = url;
|
||||
}
|
||||
}, {once: true});
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore multiple windows using the provided state.
|
||||
* @param aWindow
|
||||
@@ -3682,6 +3725,19 @@ var SessionStoreInternal = {
|
||||
this.restoreTabContent(tab, options);
|
||||
} else if (!forceOnDemand) {
|
||||
TabRestoreQueue.add(tab);
|
||||
// Check if a tab is in queue and will be restored
|
||||
// after the currently loading tabs. If so, prepare
|
||||
// a connection to host to speed up page loading.
|
||||
if (TabRestoreQueue.willRestoreSoon(tab)) {
|
||||
if (activeIndex in tabData.entries) {
|
||||
let url = tabData.entries[activeIndex].url;
|
||||
let prepared = this.prepareConnectionToHost(url);
|
||||
if (gDebuggingEnabled) {
|
||||
tab.__test_connection_prepared = prepared;
|
||||
tab.__test_connection_url = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.restoreNextTab();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -32,6 +32,7 @@ support-files =
|
||||
browser_scrollPositions_sample_frameset.html
|
||||
browser_scrollPositions_readerModeArticle.html
|
||||
browser_sessionStorage.html
|
||||
browser_speculative_connect.html
|
||||
browser_248970_b_sample.html
|
||||
browser_339445_sample.html
|
||||
browser_423132_sample.html
|
||||
@@ -258,3 +259,5 @@ skip-if = !e10s # Tabs can't crash without e10s
|
||||
[browser_cookies.js]
|
||||
[browser_cookies_legacy.js]
|
||||
[browser_cookies_privacy.js]
|
||||
[browser_speculative_connect.js]
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<header>
|
||||
<title>Dummy html page to test speculative connect</title>
|
||||
</header>
|
||||
<body>
|
||||
Hello Speculative Connect
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
const TEST_URLS = [
|
||||
"about:buildconfig",
|
||||
"http://mochi.test:8888/browser/browser/components/sessionstore/test/browser_speculative_connect.html",
|
||||
""
|
||||
];
|
||||
|
||||
/**
|
||||
* This will open tabs in browser. This will also make the last tab
|
||||
* inserted to be the selected tab.
|
||||
*/
|
||||
async function openTabs(win) {
|
||||
for (let i = 0; i < TEST_URLS.length; ++i) {
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URLS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function speculative_connect_restore_on_demand() {
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
|
||||
is(Services.prefs.getBoolPref("browser.sessionstore.restore_on_demand"), true, "We're restoring on demand");
|
||||
forgetClosedWindows();
|
||||
|
||||
// Open a new window and populate with tabs.
|
||||
let win = await promiseNewWindowLoaded();
|
||||
await openTabs(win);
|
||||
|
||||
// Close the window.
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
|
||||
// Reopen a window.
|
||||
let newWin = undoCloseWindow(0);
|
||||
// Make sure we wait until this window is restored.
|
||||
await BrowserTestUtils.waitForEvent(newWin, "load");
|
||||
await BrowserTestUtils.waitForEvent(newWin.gBrowser.tabContainer, "SSTabRestored");
|
||||
|
||||
let tabs = newWin.gBrowser.tabs;
|
||||
is(tabs.length, TEST_URLS.length + 1, "Restored right number of tabs");
|
||||
|
||||
let e = new MouseEvent("mouseover");
|
||||
|
||||
// First tab should be ignore, since it's the default blank tab when we open a new window.
|
||||
|
||||
// Trigger a mouse enter on second tab.
|
||||
tabs[1].dispatchEvent(e);
|
||||
is(tabs[1].__test_connection_prepared, false, "Second tab doesn't have a connection prepared");
|
||||
is(tabs[1].__test_connection_url, TEST_URLS[0], "Second tab has correct url");
|
||||
|
||||
// Trigger a mouse enter on third tab.
|
||||
tabs[2].dispatchEvent(e);
|
||||
is(tabs[2].__test_connection_prepared, true, "Third tab has a connection prepared");
|
||||
is(tabs[2].__test_connection_url, TEST_URLS[1], "Third tab has correct url");
|
||||
|
||||
// Last tab is the previously selected tab.
|
||||
tabs[3].dispatchEvent(e);
|
||||
is(tabs[3].__test_connection_prepared, undefined, "Previous selected tab should not have a connection prepared");
|
||||
is(tabs[3].__test_connection_url, undefined, "Previous selected tab should not have a connection prepared");
|
||||
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
});
|
||||
|
||||
add_task(async function speculative_connect_restore_automatically() {
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
|
||||
is(Services.prefs.getBoolPref("browser.sessionstore.restore_on_demand"), false, "We're restoring automatically");
|
||||
forgetClosedWindows();
|
||||
|
||||
// Open a new window and populate with tabs.
|
||||
let win = await promiseNewWindowLoaded();
|
||||
await openTabs(win);
|
||||
|
||||
// Close the window.
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
|
||||
// Reopen a window.
|
||||
let newWin = undoCloseWindow(0);
|
||||
// Make sure we wait until this window is restored.
|
||||
await BrowserTestUtils.waitForEvent(newWin, "load");
|
||||
await BrowserTestUtils.waitForEvent(newWin.gBrowser.tabContainer, "SSTabRestored");
|
||||
|
||||
let tabs = newWin.gBrowser.tabs;
|
||||
is(tabs.length, TEST_URLS.length + 1, "Restored right number of tabs");
|
||||
|
||||
// First tab is ignore, since it's the default tab open when we open new window
|
||||
|
||||
// Second tab.
|
||||
is(tabs[1].__test_connection_prepared, false, "Second tab doesn't have a connection prepared");
|
||||
is(tabs[1].__test_connection_url, TEST_URLS[0], "Second tab has correct host url");
|
||||
|
||||
// Third tab.
|
||||
is(tabs[2].__test_connection_prepared, true, "Third tab has a connection prepared");
|
||||
is(tabs[2].__test_connection_url, TEST_URLS[1], "Third tab has correct host url");
|
||||
|
||||
// Last tab is the previously selected tab.
|
||||
is(tabs[3].__test_connection_prepared, undefined, "Selected tab should not have a connection prepared");
|
||||
is(tabs[3].__test_connection_url, undefined, "Selected tab should not have a connection prepared");
|
||||
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
});
|
||||
@@ -1005,7 +1005,8 @@ this.UITour = {
|
||||
* Called before opening or after closing a highlight or info panel to see if
|
||||
* we need to open or close the appMenu to see the annotation's anchor.
|
||||
*/
|
||||
_setAppMenuStateForAnnotation(aWindow, aAnnotationType, aShouldOpenForHighlight, aCallback = null) {
|
||||
_setAppMenuStateForAnnotation(aWindow, aAnnotationType, aShouldOpenForHighlight, aTarget = null,
|
||||
aCallback = null) {
|
||||
log.debug("_setAppMenuStateForAnnotation:", aAnnotationType);
|
||||
log.debug("_setAppMenuStateForAnnotation: Menu is expected to be:", aShouldOpenForHighlight ? "open" : "closed");
|
||||
|
||||
@@ -1035,7 +1036,16 @@ this.UITour = {
|
||||
// Actually show or hide the menu
|
||||
if (this.appMenuOpenForAnnotation.size) {
|
||||
log.debug("_setAppMenuStateForAnnotation: Opening the menu");
|
||||
this.showMenu(aWindow, "appMenu", aCallback);
|
||||
this.showMenu(aWindow, "appMenu", async () => {
|
||||
// PanelMultiView's like the AppMenu might shuffle the DOM, which might result
|
||||
// in our target being invalidated if it was anonymous content (since the XBL
|
||||
// binding it belonged to got destroyed). We work around this by re-querying for
|
||||
// the node and stuffing it into the old target structure.
|
||||
log.debug("_setAppMenuStateForAnnotation: Refreshing target");
|
||||
let refreshedTarget = await this.getTarget(aWindow, aTarget.targetName);
|
||||
aTarget.node = refreshedTarget.node;
|
||||
aCallback();
|
||||
});
|
||||
} else {
|
||||
log.debug("_setAppMenuStateForAnnotation: Closing the menu");
|
||||
this.hideMenu(aWindow, "appMenu");
|
||||
@@ -1152,6 +1162,7 @@ this.UITour = {
|
||||
|
||||
this._setAppMenuStateForAnnotation(aChromeWindow, "highlight",
|
||||
this.targetIsInAppMenu(aTarget),
|
||||
aTarget,
|
||||
showHighlightPanel.bind(this));
|
||||
},
|
||||
|
||||
@@ -1281,9 +1292,17 @@ this.UITour = {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to bind the anchor argument to the showInfoPanel function call
|
||||
// after _setAppMenuStateForAnnotation has finished, since
|
||||
// _setAppMenuStateForAnnotation might have refreshed the anchor node.
|
||||
let callShowInfoPanel = () => {
|
||||
showInfoPanel.call(this, this._correctAnchor(aAnchor.node));
|
||||
};
|
||||
|
||||
this._setAppMenuStateForAnnotation(aChromeWindow, "info",
|
||||
this.targetIsInAppMenu(aAnchor),
|
||||
showInfoPanel.bind(this, this._correctAnchor(aAnchor.node)));
|
||||
aAnchor,
|
||||
callShowInfoPanel);
|
||||
},
|
||||
|
||||
isInfoOnTarget(aChromeWindow, aTargetName) {
|
||||
|
||||
@@ -15,22 +15,26 @@ var tests = [
|
||||
function test_info_customize_auto_open_close(done) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
gContentAPI.showInfo("customize", "Customization", "Customize me please!");
|
||||
UITour.getTarget(window, "customize").then((customizeTarget) => {
|
||||
waitForPopupAtAnchor(popup, customizeTarget.node, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened before the popup anchored");
|
||||
ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
|
||||
|
||||
// Move the info outside which should close the app menu.
|
||||
gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
|
||||
UITour.getTarget(window, "appMenu").then((target) => {
|
||||
waitForPopupAtAnchor(popup, target.node, function checkPanelIsClosed() {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel should have closed after the info moved elsewhere.");
|
||||
ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
|
||||
done();
|
||||
}, "Info should move to the appMenu button");
|
||||
});
|
||||
}, "Info panel should be anchored to the customize button");
|
||||
let shownPromise = promisePanelShown(window);
|
||||
shownPromise.then(() => {
|
||||
UITour.getTarget(window, "customize").then((customizeTarget) => {
|
||||
waitForPopupAtAnchor(popup, customizeTarget.node, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened before the popup anchored");
|
||||
ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
|
||||
|
||||
// Move the info outside which should close the app menu.
|
||||
gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
|
||||
UITour.getTarget(window, "appMenu").then((target) => {
|
||||
waitForPopupAtAnchor(popup, target.node, function checkPanelIsClosed() {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel should have closed after the info moved elsewhere.");
|
||||
ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
|
||||
done();
|
||||
}, "Info should move to the appMenu button");
|
||||
});
|
||||
}, "Info panel should be anchored to the customize button");
|
||||
});
|
||||
});
|
||||
},
|
||||
function test_info_customize_manual_open_close(done) {
|
||||
|
||||
22
browser/extensions/e10srollout/bootstrap.js
vendored
22
browser/extensions/e10srollout/bootstrap.js
vendored
@@ -24,17 +24,19 @@ const TEST_THRESHOLD = {
|
||||
const MULTI_EXPERIMENT = {
|
||||
"beta": { buckets: { 1: .5, 4: 1, }, // 1 process: 50%, 4 processes: 50%
|
||||
|
||||
// See below for an explanation, this only allows webextensions.
|
||||
get addonsDisableExperiment() { return getAddonsDisqualifyForMulti(); } },
|
||||
// When on the "beta" channel, getAddonsDisqualifyForMulti
|
||||
// will return true if any addon installed is not a web extension.
|
||||
// Therefore, this returns true if and only if all addons
|
||||
// installed are web extensions or if no addons are installed
|
||||
// at all.
|
||||
addonsDisableExperiment(prefix) { return getAddonsDisqualifyForMulti(); } },
|
||||
|
||||
"release": { buckets: { 1: .2, 4: 1 }, // 1 process: 20%, 4 processes: 80%
|
||||
"release": { buckets: { 1: .99, 4: 1 }, // 1 process: 99%, 4 processes: 1%
|
||||
|
||||
// When on the "release" channel, getAddonsDisqualifyForMulti
|
||||
// will return true if any addon installed is not a web extension.
|
||||
// Therefore, this returns true if and only if all addons
|
||||
// installed are web extensions or if no addons are installed
|
||||
// at all.
|
||||
get addonsDisableExperiment() { return getAddonsDisqualifyForMulti(); } }
|
||||
// We don't want to allow users with any extension
|
||||
// (webextension or otherwise in the experiment). prefix will
|
||||
// be non-empty if there is any addon.
|
||||
addonsDisableExperiment(prefix) { return !!prefix; } }
|
||||
};
|
||||
|
||||
const ADDON_ROLLOUT_POLICY = {
|
||||
@@ -183,7 +185,7 @@ function defineCohort() {
|
||||
// the default number of content processes (1 on beta) but still in the
|
||||
// test cohort.
|
||||
if (!(updateChannel in MULTI_EXPERIMENT) ||
|
||||
MULTI_EXPERIMENT[updateChannel].addonsDisableExperiment ||
|
||||
MULTI_EXPERIMENT[updateChannel].addonsDisableExperiment(cohortPrefix) ||
|
||||
!eligibleForMulti ||
|
||||
userOptedIn.multi ||
|
||||
disqualified) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>e10srollout@mozilla.org</em:id>
|
||||
<em:version>1.80</em:version>
|
||||
<em:version>1.85</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
||||
@@ -105,21 +105,24 @@ AutofillProfileAutoCompleteSearch.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._getAddresses({info, searchString}).then((addresses) => {
|
||||
let collectionName = FormAutofillUtils.isAddressField(info.fieldName) ?
|
||||
"addresses" : "creditCards";
|
||||
|
||||
this._getRecords({collectionName, info, searchString}).then((records) => {
|
||||
if (this.forceStop) {
|
||||
return;
|
||||
}
|
||||
// Sort addresses by timeLastUsed for showing the lastest used address at top.
|
||||
addresses.sort((a, b) => b.timeLastUsed - a.timeLastUsed);
|
||||
records.sort((a, b) => b.timeLastUsed - a.timeLastUsed);
|
||||
|
||||
let handler = FormAutofillContent.getFormHandler(focusedInput);
|
||||
let adaptedAddresses = handler.getAdaptedProfiles(addresses);
|
||||
let adaptedRecords = handler.getAdaptedProfiles(records);
|
||||
|
||||
let allFieldNames = FormAutofillContent.getAllFieldNames(focusedInput);
|
||||
let result = new ProfileAutoCompleteResult(searchString,
|
||||
info.fieldName,
|
||||
allFieldNames,
|
||||
adaptedAddresses,
|
||||
adaptedRecords,
|
||||
{});
|
||||
|
||||
listener.onSearchResult(this, result);
|
||||
@@ -136,27 +139,29 @@ AutofillProfileAutoCompleteSearch.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the address data from parent process for AutoComplete result.
|
||||
* Get the records from parent process for AutoComplete result.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} data
|
||||
* Parameters for querying the corresponding result.
|
||||
* @param {string} data.collectionName
|
||||
* The name used to specify which collection to retrieve records.
|
||||
* @param {string} data.searchString
|
||||
* The typed string for filtering out the matched address.
|
||||
* The typed string for filtering out the matched records.
|
||||
* @param {string} data.info
|
||||
* The input autocomplete property's information.
|
||||
* @returns {Promise}
|
||||
* Promise that resolves when addresses returned from parent process.
|
||||
*/
|
||||
_getAddresses(data) {
|
||||
this.log.debug("_getAddresses with data:", data);
|
||||
_getRecords(data) {
|
||||
this.log.debug("_getRecords with data:", data);
|
||||
return new Promise((resolve) => {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
||||
Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
|
||||
resolve(result.data);
|
||||
});
|
||||
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", data);
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", data);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -80,7 +80,7 @@ FormAutofillParent.prototype = {
|
||||
async init() {
|
||||
Services.obs.addObserver(this, "advanced-pane-loaded");
|
||||
Services.ppmm.addMessageListener("FormAutofill:InitStorage", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:GetAddresses", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:GetRecords", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:OpenPreferences", this);
|
||||
@@ -181,8 +181,8 @@ FormAutofillParent.prototype = {
|
||||
this.profileStorage.initialize();
|
||||
break;
|
||||
}
|
||||
case "FormAutofill:GetAddresses": {
|
||||
this._getAddresses(data, target);
|
||||
case "FormAutofill:GetRecords": {
|
||||
this._getRecords(data, target);
|
||||
break;
|
||||
}
|
||||
case "FormAutofill:SaveAddress": {
|
||||
@@ -217,7 +217,7 @@ FormAutofillParent.prototype = {
|
||||
this.profileStorage._saveImmediately();
|
||||
|
||||
Services.ppmm.removeMessageListener("FormAutofill:InitStorage", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:GetAddresses", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:GetRecords", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:SaveAddress", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:RemoveAddresses", this);
|
||||
Services.obs.removeObserver(this, "advanced-pane-loaded");
|
||||
@@ -225,27 +225,32 @@ FormAutofillParent.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the address data from profile store and return addresses back to content
|
||||
* Get the records from profile store and return results back to content
|
||||
* process.
|
||||
*
|
||||
* @private
|
||||
* @param {string} data.collectionName
|
||||
* The name used to specify which collection to retrieve records.
|
||||
* @param {string} data.searchString
|
||||
* The typed string for filtering out the matched address.
|
||||
* The typed string for filtering out the matched records.
|
||||
* @param {string} data.info
|
||||
* The input autocomplete property's information.
|
||||
* @param {nsIFrameMessageManager} target
|
||||
* Content's message manager.
|
||||
*/
|
||||
_getAddresses({searchString, info}, target) {
|
||||
let addresses = [];
|
||||
_getRecords({collectionName, searchString, info}, target) {
|
||||
let records;
|
||||
let collection = this.profileStorage[collectionName];
|
||||
|
||||
if (info && info.fieldName) {
|
||||
addresses = this.profileStorage.addresses.getByFilter({searchString, info});
|
||||
if (!collection) {
|
||||
records = [];
|
||||
} else if (info && info.fieldName) {
|
||||
records = collection.getByFilter({searchString, info});
|
||||
} else {
|
||||
addresses = this.profileStorage.addresses.getAll();
|
||||
records = collection.getAll();
|
||||
}
|
||||
|
||||
target.sendAsyncMessage("FormAutofill:Addresses", addresses);
|
||||
target.sendAsyncMessage("FormAutofill:Records", records);
|
||||
},
|
||||
|
||||
_updateSavedFieldNames() {
|
||||
@@ -256,12 +261,14 @@ FormAutofillParent.prototype = {
|
||||
Services.ppmm.initialProcessData.autofillSavedFieldNames.clear();
|
||||
}
|
||||
|
||||
this.profileStorage.addresses.getAll().forEach((address) => {
|
||||
Object.keys(address).forEach((fieldName) => {
|
||||
if (!address[fieldName]) {
|
||||
return;
|
||||
}
|
||||
Services.ppmm.initialProcessData.autofillSavedFieldNames.add(fieldName);
|
||||
["addresses", "creditCards"].forEach(c => {
|
||||
this.profileStorage[c].getAll().forEach((record) => {
|
||||
Object.keys(record).forEach((fieldName) => {
|
||||
if (!record[fieldName]) {
|
||||
return;
|
||||
}
|
||||
Services.ppmm.initialProcessData.autofillSavedFieldNames.add(fieldName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -299,9 +306,13 @@ FormAutofillParent.prototype = {
|
||||
}
|
||||
changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid));
|
||||
});
|
||||
// Address should be updated
|
||||
Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill_update", 1);
|
||||
return;
|
||||
}
|
||||
this.profileStorage.addresses.notifyUsed(address.guid);
|
||||
// Address is merged successfully
|
||||
Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1);
|
||||
} else {
|
||||
let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record);
|
||||
if (!changedGUIDs.length) {
|
||||
@@ -320,6 +331,9 @@ FormAutofillParent.prototype = {
|
||||
target.ownerGlobal.openPreferences("panePrivacy",
|
||||
{origin: "autofillDoorhanger"});
|
||||
});
|
||||
} else {
|
||||
// We want to exclude the first time form filling.
|
||||
Services.telemetry.scalarAdd("formautofill.addresses.fill_type_manual", 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -63,7 +63,7 @@ ManageProfileDialog.prototype = {
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadAddresses() {
|
||||
return this.getAddresses().then(addresses => {
|
||||
return this.getRecords({collectionName: "addresses"}).then(addresses => {
|
||||
log.debug("addresses:", addresses);
|
||||
// Sort by last modified time starting with most recent
|
||||
addresses.sort((a, b) => b.timeLastModified - a.timeLastModified);
|
||||
@@ -73,17 +73,27 @@ ManageProfileDialog.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get addresses from storage.
|
||||
* Get records from storage.
|
||||
*
|
||||
* @returns {promise}
|
||||
* @private
|
||||
* @param {Object} data
|
||||
* Parameters for querying the corresponding result.
|
||||
* @param {string} data.collectionName
|
||||
* The name used to specify which collection to retrieve records.
|
||||
* @param {string} data.searchString
|
||||
* The typed string for filtering out the matched records.
|
||||
* @param {string} data.info
|
||||
* The input autocomplete property's information.
|
||||
* @returns {Promise}
|
||||
* Promise that resolves when addresses returned from parent process.
|
||||
*/
|
||||
getAddresses() {
|
||||
getRecords(data) {
|
||||
return new Promise(resolve => {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
||||
Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
|
||||
resolve(result.data);
|
||||
});
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", {});
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", data);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ const TEST_SELECTORS = {
|
||||
|
||||
const DIALOG_SIZE = "width=600,height=400";
|
||||
|
||||
function waitForAddresses() {
|
||||
function waitForRecords() {
|
||||
return new Promise(resolve => {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
||||
Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
|
||||
// Wait for the next tick for elements to get rendered.
|
||||
SimpleTest.executeSoon(resolve.bind(null, result.data));
|
||||
});
|
||||
@@ -54,7 +54,7 @@ add_task(async function test_removingSingleAndMultipleProfiles() {
|
||||
await saveAddress(TEST_ADDRESS_3);
|
||||
|
||||
let win = window.openDialog(MANAGE_PROFILES_DIALOG_URL, null, DIALOG_SIZE);
|
||||
await waitForAddresses();
|
||||
await waitForRecords();
|
||||
|
||||
let selAddresses = win.document.querySelector(TEST_SELECTORS.selAddresses);
|
||||
let btnRemove = win.document.querySelector(TEST_SELECTORS.btnRemove);
|
||||
@@ -66,7 +66,7 @@ add_task(async function test_removingSingleAndMultipleProfiles() {
|
||||
is(btnRemove.disabled, false, "Remove button enabled");
|
||||
is(btnEdit.disabled, false, "Edit button enabled");
|
||||
EventUtils.synthesizeMouseAtCenter(btnRemove, {}, win);
|
||||
await waitForAddresses();
|
||||
await waitForRecords();
|
||||
is(selAddresses.length, 2, "Two addresses left");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(selAddresses.children[0], {}, win);
|
||||
@@ -75,7 +75,7 @@ add_task(async function test_removingSingleAndMultipleProfiles() {
|
||||
is(btnEdit.disabled, true, "Edit button disabled when multi-select");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(btnRemove, {}, win);
|
||||
await waitForAddresses();
|
||||
await waitForRecords();
|
||||
is(selAddresses.length, 0, "All addresses are removed");
|
||||
|
||||
win.close();
|
||||
@@ -83,16 +83,16 @@ add_task(async function test_removingSingleAndMultipleProfiles() {
|
||||
|
||||
add_task(async function test_profilesDialogWatchesStorageChanges() {
|
||||
let win = window.openDialog(MANAGE_PROFILES_DIALOG_URL, null, DIALOG_SIZE);
|
||||
await waitForAddresses();
|
||||
await waitForRecords();
|
||||
|
||||
let selAddresses = win.document.querySelector(TEST_SELECTORS.selAddresses);
|
||||
|
||||
await saveAddress(TEST_ADDRESS_1);
|
||||
let addresses = await waitForAddresses();
|
||||
let addresses = await waitForRecords();
|
||||
is(selAddresses.length, 1, "One address is shown");
|
||||
|
||||
await removeAddresses([addresses[0].guid]);
|
||||
await waitForAddresses();
|
||||
await waitForRecords();
|
||||
is(selAddresses.length, 0, "Address is removed");
|
||||
win.close();
|
||||
});
|
||||
|
||||
@@ -64,16 +64,20 @@ async function openPopupOn(browser, selector) {
|
||||
await expectPopupOpen(browser);
|
||||
}
|
||||
|
||||
function getAddresses() {
|
||||
function getRecords(data) {
|
||||
return new Promise(resolve => {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
||||
Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
|
||||
resolve(result.data);
|
||||
});
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", {});
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", data);
|
||||
});
|
||||
}
|
||||
|
||||
function getAddresses() {
|
||||
return getRecords({collectionName: "addresses"});
|
||||
}
|
||||
|
||||
function saveAddress(address) {
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:SaveAddress", {address});
|
||||
return TestUtils.topicObserved("formautofill-storage-changed");
|
||||
|
||||
@@ -11,15 +11,15 @@ let {profileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {
|
||||
|
||||
var ParentUtils = {
|
||||
cleanUpAddress() {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
||||
Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
|
||||
|
||||
let addresses = result.data;
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:RemoveAddresses",
|
||||
{guids: addresses.map(address => address.guid)});
|
||||
});
|
||||
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", {searchString: ""});
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", {searchString: "", collectionName: "addresses"});
|
||||
},
|
||||
|
||||
updateAddress(type, chromeMsg, msgData, contentMsg) {
|
||||
@@ -60,8 +60,8 @@ var ParentUtils = {
|
||||
},
|
||||
|
||||
checkAddresses({expectedAddresses}) {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
||||
Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
|
||||
let addresses = result.data;
|
||||
if (addresses.length !== expectedAddresses.length) {
|
||||
sendAsyncMessage("FormAutofillTest:areAddressesMatching", false);
|
||||
@@ -82,7 +82,7 @@ var ParentUtils = {
|
||||
sendAsyncMessage("FormAutofillTest:areAddressesMatching", true);
|
||||
});
|
||||
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", {searchString: ""});
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", {searchString: "", collectionName: "addresses"});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
50
browser/extensions/formautofill/test/unit/test_getRecords.js
Normal file
50
browser/extensions/formautofill/test/unit/test_getRecords.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Test for make sure getRecords can retrieve right collection from storag.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://formautofill/FormAutofillParent.jsm");
|
||||
Cu.import("resource://formautofill/ProfileStorage.jsm");
|
||||
|
||||
add_task(async function test_getRecords() {
|
||||
let formAutofillParent = new FormAutofillParent();
|
||||
|
||||
await formAutofillParent.init();
|
||||
await formAutofillParent.profileStorage.initialize();
|
||||
|
||||
let fakeResult = {
|
||||
addresses: [{
|
||||
"given-name": "Timothy",
|
||||
"additional-name": "John",
|
||||
"family-name": "Berners-Lee",
|
||||
"organization": "World Wide Web Consortium",
|
||||
}],
|
||||
creditCards: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
}],
|
||||
};
|
||||
|
||||
["addresses", "creditCards", "nonExisting"].forEach(collectionName => {
|
||||
let collection = profileStorage[collectionName];
|
||||
let expectedResult = fakeResult[collectionName] || [];
|
||||
let target = {
|
||||
sendAsyncMessage: function sendAsyncMessage(msg, payload) {},
|
||||
};
|
||||
let mock = sinon.mock(target);
|
||||
mock.expects("sendAsyncMessage").once().withExactArgs("FormAutofill:Records", expectedResult);
|
||||
|
||||
if (collection) {
|
||||
sinon.stub(collection, "getAll");
|
||||
collection.getAll.returns(expectedResult);
|
||||
}
|
||||
formAutofillParent._getRecords({collectionName}, target);
|
||||
mock.verify();
|
||||
if (collection) {
|
||||
do_check_eq(collection.getAll.called, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -27,6 +27,7 @@ support-files =
|
||||
[test_getCategoriesFromFieldNames.js]
|
||||
[test_getFormInputDetails.js]
|
||||
[test_getInfo.js]
|
||||
[test_getRecords.js]
|
||||
[test_isCJKName.js]
|
||||
[test_isFieldEligibleForAutofill.js]
|
||||
[test_markAsAutofillField.js]
|
||||
|
||||
73
browser/extensions/onboarding/bootstrap.js
vendored
73
browser/extensions/onboarding/bootstrap.js
vendored
@@ -13,10 +13,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
const BROWSER_READY_NOTIFICATION = "final-ui-startup";
|
||||
const BROWSER_READY_NOTIFICATION = "browser-delayed-startup-finished";
|
||||
const BROWSER_SESSION_STORE_NOTIFICATION = "sessionstore-windows-restored";
|
||||
const PREF_WHITELIST = [
|
||||
"browser.onboarding.enabled",
|
||||
"browser.onboarding.hidden",
|
||||
@@ -27,12 +28,14 @@ const PREF_WHITELIST = [
|
||||
];
|
||||
|
||||
[
|
||||
"onboarding-tour-private-browsing",
|
||||
"onboarding-tour-addons",
|
||||
"onboarding-tour-customize",
|
||||
"onboarding-tour-default-browser",
|
||||
"onboarding-tour-library",
|
||||
"onboarding-tour-performance",
|
||||
"onboarding-tour-private-browsing",
|
||||
"onboarding-tour-search",
|
||||
"onboarding-tour-singlesearch",
|
||||
"onboarding-tour-sync",
|
||||
].forEach(tourId => PREF_WHITELIST.push(`browser.onboarding.tour.${tourId}.completed`));
|
||||
|
||||
@@ -69,6 +72,54 @@ function initContentMessageListener() {
|
||||
});
|
||||
}
|
||||
|
||||
let syncTourChecker = {
|
||||
registered: false,
|
||||
|
||||
observe() {
|
||||
this.setComplete();
|
||||
},
|
||||
|
||||
init() {
|
||||
if (Services.prefs.getBoolPref("browser.onboarding.tour.onboarding-tour-sync.completed", false)) {
|
||||
return;
|
||||
}
|
||||
// Check if we've already logged in at startup.
|
||||
fxAccounts.getSignedInUser().then(user => {
|
||||
if (user) {
|
||||
this.setComplete();
|
||||
return;
|
||||
}
|
||||
// Observe for login action if we haven't logged in yet.
|
||||
this.register();
|
||||
});
|
||||
},
|
||||
|
||||
register() {
|
||||
if (this.registered) {
|
||||
return;
|
||||
}
|
||||
Services.obs.addObserver(this, "fxaccounts:onverified");
|
||||
this.registered = true;
|
||||
},
|
||||
|
||||
setComplete() {
|
||||
Services.prefs.setBoolPref("browser.onboarding.tour.onboarding-tour-sync.completed", true);
|
||||
this.unregister();
|
||||
},
|
||||
|
||||
unregister() {
|
||||
if (!this.registered) {
|
||||
return;
|
||||
}
|
||||
Services.obs.removeObserver(this, "fxaccounts:onverified");
|
||||
this.registered = false;
|
||||
},
|
||||
|
||||
uninit() {
|
||||
this.unregister();
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* onBrowserReady - Continues startup of the add-on after browser is ready.
|
||||
*/
|
||||
@@ -87,8 +138,15 @@ function observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case BROWSER_READY_NOTIFICATION:
|
||||
Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
|
||||
// Avoid running synchronously during this event that's used for timing
|
||||
setTimeout(() => onBrowserReady());
|
||||
onBrowserReady();
|
||||
break;
|
||||
case BROWSER_SESSION_STORE_NOTIFICATION:
|
||||
Services.obs.removeObserver(observe, BROWSER_SESSION_STORE_NOTIFICATION);
|
||||
// Postpone Firefox account checking until "before handling user events"
|
||||
// phase to meet performance criteria. The reason we don't postpone the
|
||||
// whole onBrowserReady here is because in that way we will miss onload
|
||||
// events for onboarding.js.
|
||||
Services.tm.idleDispatchToMainThread(() => syncTourChecker.init());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -101,8 +159,10 @@ function startup(aData, aReason) {
|
||||
// Only start Onboarding when the browser UI is ready
|
||||
if (aReason === APP_STARTUP || aReason === ADDON_INSTALL) {
|
||||
Services.obs.addObserver(observe, BROWSER_READY_NOTIFICATION);
|
||||
Services.obs.addObserver(observe, BROWSER_SESSION_STORE_NOTIFICATION);
|
||||
} else {
|
||||
onBrowserReady();
|
||||
syncTourChecker.init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,4 +171,5 @@ function shutdown(aData, aReason) {
|
||||
if (waitingForBrowserReady) {
|
||||
Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
|
||||
}
|
||||
syncTourChecker.uninit();
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 92 92" xmlns="http://www.w3.org/2000/svg"><title>Tip / Icon / Performance</title><g id="Symbols" fill="none" fill-rule="evenodd"><g id="Tip-/-Icon-/-Performance" fill="#0A84FF"><path d="M47.237 53.003L47 53c-6.075 0-11 4.925-11 11s4.925 11 11 11c4.453 0 8.287-2.645 10.018-6.45 1.888-3.525 2.97-7.84 8.397-12.066 7.422-5.778 15.097-10.033 14.61-11.098-.485-1.06-8.04 2.724-16.675 5.79-7.916 2.812-14.765 1.706-16.113 1.827zm-36.467 31.3C4.05 76.316 0 66.015 0 54.768 0 29.4 20.593 8.838 46 8.838c25.404 0 46 20.563 46 45.93 0 11.247-4.05 21.548-10.77 29.535H10.77zM46 21.698c-1.016 0-1.84 1.646-1.84 3.674s.824 3.675 1.84 3.675 1.84-1.647 1.84-3.675c0-2.028-.824-3.674-1.84-3.674zm34.96 40.418c0-1.016-1.65-1.837-3.68-1.837-2.03 0-3.68.82-3.68 1.836s1.65 1.837 3.68 1.837c2.03 0 3.68-.82 3.68-1.837zm-62.364 0c0-1.016-1.65-1.837-3.68-1.837-2.032 0-3.68.82-3.68 1.836s1.648 1.837 3.68 1.837c2.03 0 3.68-.82 3.68-1.837zm7.664-23.133c.73-.706.18-2.46-1.232-3.92s-3.15-2.072-3.88-1.366c-.73.704-.178 2.458 1.234 3.92 1.41 1.457 3.148 2.07 3.878 1.366zm46-5.287c-.73-.706-2.468-.094-3.88 1.367-1.41 1.46-1.962 3.215-1.232 3.92.73.704 2.47.092 3.88-1.368 1.412-1.46 1.964-3.214 1.233-3.92z" id="Combined-Shape"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 92 92" xmlns="http://www.w3.org/2000/svg"><title>Tip / Icon / Performance</title><g id="Symbols" fill="none" fill-rule="evenodd"><g id="Tip-/-Icon-/-Performance" fill="#3E3D40"><path d="M47.237 53.003L47 53c-6.075 0-11 4.925-11 11s4.925 11 11 11c4.453 0 8.287-2.645 10.018-6.45 1.888-3.525 2.97-7.84 8.397-12.066 7.422-5.778 15.097-10.033 14.61-11.098-.485-1.06-8.04 2.724-16.675 5.79-7.916 2.812-14.765 1.706-16.113 1.827zm-36.467 31.3C4.05 76.316 0 66.015 0 54.768 0 29.4 20.593 8.838 46 8.838c25.404 0 46 20.563 46 45.93 0 11.247-4.05 21.548-10.77 29.535H10.77zM46 21.698c-1.016 0-1.84 1.646-1.84 3.674s.824 3.675 1.84 3.675 1.84-1.647 1.84-3.675c0-2.028-.824-3.674-1.84-3.674zm34.96 40.418c0-1.016-1.65-1.837-3.68-1.837-2.03 0-3.68.82-3.68 1.836s1.65 1.837 3.68 1.837c2.03 0 3.68-.82 3.68-1.837zm-62.364 0c0-1.016-1.65-1.837-3.68-1.837-2.032 0-3.68.82-3.68 1.836s1.648 1.837 3.68 1.837c2.03 0 3.68-.82 3.68-1.837zm7.664-23.133c.73-.706.18-2.46-1.232-3.92s-3.15-2.072-3.88-1.366c-.73.704-.178 2.458 1.234 3.92 1.41 1.457 3.148 2.07 3.878 1.366zm46-5.287c-.73-.706-2.468-.094-3.88 1.367-1.41 1.46-1.962 3.215-1.232 3.92.73.704 2.47.092 3.88-1.368 1.412-1.46 1.964-3.214 1.233-3.92z" id="Combined-Shape"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -38,6 +38,9 @@ document.getElementById("onboarding-overlay")
|
||||
case "onboarding-tour-search-button":
|
||||
Mozilla.UITour.openSearchPanel(() => {});
|
||||
break;
|
||||
case "onboarding-tour-singlesearch-button":
|
||||
Mozilla.UITour.showMenu("urlbar");
|
||||
break;
|
||||
case "onboarding-tour-sync-button":
|
||||
let emailInput = document.getElementById("onboarding-tour-sync-email-input");
|
||||
if (emailInput.checkValidity()) {
|
||||
|
||||
@@ -23,30 +23,35 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
#onboarding-overlay-icon {
|
||||
width: 36px;
|
||||
height: 29px;
|
||||
#onboarding-overlay-button {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 30px;
|
||||
offset-inline-start: 30px;
|
||||
background: url("img/overlay-icon.svg") no-repeat;
|
||||
border: none;
|
||||
/* Set to none so no grey contrast background in the high-contrast mode */
|
||||
background: none;
|
||||
}
|
||||
|
||||
#onboarding-overlay-button-icon {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
#onboarding-notification-icon::after,
|
||||
#onboarding-overlay-icon::after {
|
||||
#onboarding-overlay-button::after {
|
||||
background: #5ce6e6;
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
border: 1px solid #fff;
|
||||
text-align: center;
|
||||
color: #10404a;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
#onboarding-overlay-icon::after {
|
||||
#onboarding-overlay-button::after {
|
||||
content: attr(aria-label);
|
||||
top: -6px;
|
||||
offset-inline-start: 32px;
|
||||
offset-inline-start: 39px;
|
||||
border-radius: 22px;
|
||||
padding: 5px 8px;
|
||||
min-width: 100px;
|
||||
@@ -57,29 +62,35 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#onboarding-overlay-close-btn,
|
||||
#onboarding-notification-close-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
offset-inline-end: 15px;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url(chrome://browser/skin/sidebar/close.svg);
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
padding: 12px;
|
||||
.onboarding-close-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
offset-inline-end: 15px;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
background: var(--onboarding-overlay-dialog-background-color);
|
||||
}
|
||||
|
||||
.onboarding-close-btn::before {
|
||||
content: url(chrome://browser/skin/sidebar/close.svg);
|
||||
display: block;
|
||||
margin-top: -8px;
|
||||
margin-inline-start: -8px;
|
||||
}
|
||||
|
||||
#onboarding-overlay-close-btn:hover,
|
||||
.onboarding-close-btn:hover,
|
||||
#onboarding-notification-close-btn:hover {
|
||||
background-color: rgba(204, 204, 204, 0.6);
|
||||
}
|
||||
|
||||
#onboarding-overlay.onboarding-opened > #onboarding-overlay-dialog {
|
||||
--onboarding-overlay-dialog-background-color: #f5f5f7;
|
||||
width: 960px;
|
||||
height: 510px;
|
||||
background: #f5f5f7;
|
||||
background: var(--onboarding-overlay-dialog-background-color);
|
||||
border: 1px solid rgba(9, 6, 13, 0.1); /* #09060D, 0.1 opacity */
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
@@ -135,10 +146,13 @@
|
||||
}
|
||||
|
||||
#onboarding-tour-list > li {
|
||||
--padding-inline-start: 49px;
|
||||
--padding-top: 14px;
|
||||
--padding-bottom: 14px;
|
||||
list-style: none;
|
||||
padding-inline-start: 49px;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
padding-inline-start: var(--padding-inline-start);
|
||||
padding-top: var(--padding-top);
|
||||
padding-bottom: var(--padding-bottom);
|
||||
margin-inline-start: 16px;
|
||||
margin-bottom: 9px;
|
||||
background-repeat: no-repeat;
|
||||
@@ -161,12 +175,19 @@
|
||||
}
|
||||
|
||||
#onboarding-tour-list > li.onboarding-complete {
|
||||
padding-inline-start: 29px;
|
||||
--padding-inline-start: 29px;
|
||||
}
|
||||
|
||||
#onboarding-tour-list > li.onboarding-active,
|
||||
#onboarding-tour-list > li:hover {
|
||||
color: #0A84FF;
|
||||
/* With 1px transparent border, could see a border in the high-constrast mode */
|
||||
border: 1px solid transparent;
|
||||
/* Substract 1px for the 1px transparent or a 1px shift would happen */
|
||||
padding-inline-start: calc(var(--padding-inline-start) - 1px);
|
||||
padding-top: calc(var(--padding-top) - 1px);
|
||||
padding-bottom: calc(var(--padding-bottom) - 1px);
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* Default browser tour */
|
||||
@@ -283,7 +304,8 @@
|
||||
font-weight: 600;
|
||||
line-height: 21px;
|
||||
background: #0a84ff;
|
||||
border: none;
|
||||
/* With 1px transparent border, could see a border in the high-constrast mode */
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
color: #fff;
|
||||
float: inline-end;
|
||||
@@ -307,16 +329,20 @@
|
||||
}
|
||||
|
||||
/* Tour Icons */
|
||||
#onboarding-tour-search {
|
||||
#onboarding-tour-search,
|
||||
#onboarding-tour-singlesearch {
|
||||
background-image: url("img/icons_search.svg");
|
||||
}
|
||||
|
||||
#onboarding-tour-search.onboarding-active,
|
||||
#onboarding-tour-search:hover {
|
||||
#onboarding-tour-search:hover,
|
||||
#onboarding-tour-singlesearch.onboarding-active,
|
||||
#onboarding-tour-singlesearch:hover {
|
||||
background-image: url("img/icons_search-colored.svg");
|
||||
}
|
||||
|
||||
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-search] #onboarding-notification-tour-icon {
|
||||
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-search] #onboarding-notification-tour-icon,
|
||||
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-singlesearch] #onboarding-notification-tour-icon {
|
||||
background-image: url("img/icons_search-notification.svg");
|
||||
}
|
||||
|
||||
@@ -396,8 +422,24 @@
|
||||
background-image: url("img/icons_search-colored.svg");
|
||||
}
|
||||
|
||||
#onboarding-tour-performance {
|
||||
background-image: url("img/icons_performance.svg");
|
||||
}
|
||||
|
||||
#onboarding-tour-performance.onboarding-active,
|
||||
#onboarding-tour-performance:hover {
|
||||
background-image: url("img/icons_performance-colored.svg");
|
||||
}
|
||||
|
||||
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-performance] #onboarding-notification-tour-icon {
|
||||
/* TODO: Placeholder icon. It should be replaced upon assets are available.
|
||||
This is tracking in Bug 1382520. */
|
||||
background-image: url("img/icons_sync-notification.svg");
|
||||
}
|
||||
|
||||
/* Tour Notifications */
|
||||
#onboarding-notification-bar {
|
||||
--onboarding-notification-bar-background-color: rgba(255, 255, 255, 0.97);
|
||||
position: fixed;
|
||||
z-index: 20998; /* We want this always under #onboarding-overlay */
|
||||
left: 0;
|
||||
@@ -405,7 +447,7 @@
|
||||
width: 100%;
|
||||
height: 122px;
|
||||
min-width: 640px;
|
||||
background: rgba(255, 255, 255, 0.97);
|
||||
background: var(--onboarding-notification-bar-background-color);
|
||||
border-top: 2px solid #e9e9e9;
|
||||
transition: transform 0.8s;
|
||||
transform: translateY(122px);
|
||||
@@ -436,15 +478,14 @@
|
||||
--vpadding: 3px;
|
||||
content: attr(data-tooltip);
|
||||
top: 0;
|
||||
offset-inline-start: 68px;
|
||||
offset-inline-start: 73px;
|
||||
line-height: calc(var(--height) - var(--vpadding) * 2);
|
||||
border-radius: calc(var(--height) / 2);
|
||||
padding: var(--vpadding) 10px;
|
||||
}
|
||||
|
||||
#onboarding-notification-close-btn {
|
||||
background-color: rgba(255, 255, 255, 0.97);
|
||||
border: none;
|
||||
background: var(--onboarding-notification-bar-background-color);
|
||||
position: absolute;
|
||||
offset-block-start: 50%;
|
||||
offset-inline-end: 34px;
|
||||
@@ -489,7 +530,8 @@
|
||||
|
||||
#onboarding-notification-action-btn {
|
||||
background: #0a84ff;
|
||||
border: none;
|
||||
/* With 1px transparent border, could see a border in the high-constrast mode */
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -250,6 +250,59 @@ var onboardingTourset = {
|
||||
return div;
|
||||
},
|
||||
},
|
||||
"singlesearch": {
|
||||
id: "onboarding-tour-singlesearch",
|
||||
tourNameId: "onboarding.tour-singlesearch",
|
||||
getNotificationStrings(bundle) {
|
||||
return {
|
||||
title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-singlesearch.title"),
|
||||
message: bundle.GetStringFromName("onboarding.notification.onboarding-tour-singlesearch.message"),
|
||||
button: bundle.GetStringFromName("onboarding.button.learnMore"),
|
||||
};
|
||||
},
|
||||
getPage(win, bundle) {
|
||||
let div = win.document.createElement("div");
|
||||
div.innerHTML = `
|
||||
<section class="onboarding-tour-description">
|
||||
<h1 data-l10n-id="onboarding.tour-singlesearch.title"></h1>
|
||||
<p data-l10n-id="onboarding.tour-singlesearch.description"></p>
|
||||
</section>
|
||||
<section class="onboarding-tour-content">
|
||||
<img src="resource://onboarding/img/figure_search.svg" role="presentation"/>
|
||||
</section>
|
||||
<aside class="onboarding-tour-button-container">
|
||||
<button id="onboarding-tour-singlesearch-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-singlesearch.button"></button>
|
||||
</aside>
|
||||
`;
|
||||
return div;
|
||||
},
|
||||
},
|
||||
"performance": {
|
||||
id: "onboarding-tour-performance",
|
||||
tourNameId: "onboarding.tour-performance",
|
||||
getNotificationStrings(bundle) {
|
||||
return {
|
||||
title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-performance.title"),
|
||||
message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-performance.message", [BRAND_SHORT_NAME], 1),
|
||||
button: bundle.GetStringFromName("onboarding.button.learnMore"),
|
||||
};
|
||||
},
|
||||
getPage(win, bundle) {
|
||||
let div = win.document.createElement("div");
|
||||
// TODO: The content image is a placeholder. It should be replaced upon assets are available.
|
||||
// This is tracking in Bug 1382520.
|
||||
div.innerHTML = `
|
||||
<section class="onboarding-tour-description">
|
||||
<h1 data-l10n-id="onboarding.tour-performance.title"></h1>
|
||||
<p data-l10n-id="onboarding.tour-performance.description"></p>
|
||||
</section>
|
||||
<section class="onboarding-tour-content">
|
||||
<img src="resource://onboarding/img/figure_sync.svg" role="presentation"/>
|
||||
</section>
|
||||
`;
|
||||
return div;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -264,8 +317,9 @@ class Onboarding {
|
||||
async init(contentWindow) {
|
||||
this._window = contentWindow;
|
||||
this._tours = [];
|
||||
this._tourType = Services.prefs.getStringPref("browser.onboarding.tour-type", "update");
|
||||
|
||||
let tourIds = this._getTourIDList(Services.prefs.getStringPref("browser.onboarding.tour-type", "update"));
|
||||
let tourIds = this._getTourIDList();
|
||||
tourIds.forEach(tourId => {
|
||||
if (onboardingTourset[tourId]) {
|
||||
this._tours.push(onboardingTourset[tourId]);
|
||||
@@ -311,7 +365,7 @@ class Onboarding {
|
||||
this._tourItems = [];
|
||||
this._tourPages = [];
|
||||
|
||||
this._overlayIcon = this._renderOverlayIcon();
|
||||
this._overlayIcon = this._renderOverlayButton();
|
||||
this._overlayIcon.addEventListener("click", this);
|
||||
this._window.document.body.appendChild(this._overlayIcon);
|
||||
|
||||
@@ -326,8 +380,8 @@ class Onboarding {
|
||||
this._window.requestIdleCallback(() => this._initNotification());
|
||||
}
|
||||
|
||||
_getTourIDList(tourType) {
|
||||
let tours = Services.prefs.getStringPref(`browser.onboarding.${tourType}tour`, "");
|
||||
_getTourIDList() {
|
||||
let tours = Services.prefs.getStringPref(`browser.onboarding.${this._tourType}tour`, "");
|
||||
return tours.split(",").filter(tourId => tourId !== "").map(tourId => tourId.trim());
|
||||
}
|
||||
|
||||
@@ -400,7 +454,7 @@ class Onboarding {
|
||||
}
|
||||
|
||||
switch (evt.target.id) {
|
||||
case "onboarding-overlay-icon":
|
||||
case "onboarding-overlay-button":
|
||||
case "onboarding-overlay-close-btn":
|
||||
// If the clicking target is directly on the outer-most overlay,
|
||||
// that means clicking outside the tour content area.
|
||||
@@ -418,6 +472,12 @@ class Onboarding {
|
||||
this.gotoPage(tourId);
|
||||
this._removeTourFromNotificationQueue(tourId);
|
||||
break;
|
||||
// These tours are tagged completed instantly upon showing.
|
||||
case "onboarding-tour-default-browser":
|
||||
case "onboarding-tour-sync":
|
||||
case "onboarding-tour-performance":
|
||||
this.setToursCompleted([ evt.target.id ]);
|
||||
break;
|
||||
}
|
||||
let classList = evt.target.classList;
|
||||
if (classList.contains("onboarding-tour-item")) {
|
||||
@@ -679,9 +739,12 @@ class Onboarding {
|
||||
</div>
|
||||
<button id="onboarding-notification-action-btn"></button>
|
||||
</section>
|
||||
<button id="onboarding-notification-close-btn"></button>
|
||||
<button id="onboarding-notification-close-btn" class="onboarding-close-btn"></button>
|
||||
`;
|
||||
let toolTip = this._bundle.formatStringFromName("onboarding.notification-icon-tool-tip", [BRAND_SHORT_NAME], 1);
|
||||
let toolTip = this._bundle.formatStringFromName(
|
||||
this._tourType === "new" ? "onboarding.notification-icon-tool-tip" :
|
||||
"onboarding.notification-icon-tooltip-updated",
|
||||
[BRAND_SHORT_NAME], 1);
|
||||
div.querySelector("#onboarding-notification-icon").setAttribute("data-tooltip", toolTip);
|
||||
return div;
|
||||
}
|
||||
@@ -707,7 +770,7 @@ class Onboarding {
|
||||
// The security should be fine because this is not from an external input.
|
||||
div.innerHTML = `
|
||||
<div id="onboarding-overlay-dialog">
|
||||
<span id="onboarding-overlay-close-btn"></span>
|
||||
<button id="onboarding-overlay-close-btn" class="onboarding-close-btn"></button>
|
||||
<header id="onboarding-header"></header>
|
||||
<nav>
|
||||
<ul id="onboarding-tour-list"></ul>
|
||||
@@ -725,12 +788,18 @@ class Onboarding {
|
||||
return div;
|
||||
}
|
||||
|
||||
_renderOverlayIcon() {
|
||||
let img = this._window.document.createElement("div");
|
||||
let tooltip = this._bundle.formatStringFromName("onboarding.overlay-icon-tooltip", [BRAND_SHORT_NAME], 1);
|
||||
img.setAttribute("aria-label", tooltip);
|
||||
img.id = "onboarding-overlay-icon";
|
||||
return img;
|
||||
_renderOverlayButton() {
|
||||
let button = this._window.document.createElement("button");
|
||||
let tooltipStringId = this._tourType === "new" ?
|
||||
"onboarding.overlay-icon-tooltip" : "onboarding.overlay-icon-tooltip-updated";
|
||||
let tooltip = this._bundle.formatStringFromName(tooltipStringId, [BRAND_SHORT_NAME], 1);
|
||||
button.setAttribute("aria-label", tooltip);
|
||||
button.id = "onboarding-overlay-button";
|
||||
let img = this._window.document.createElement("img");
|
||||
img.id = "onboarding-overlay-button-icon";
|
||||
img.src = "resource://onboarding/img/overlay-icon.svg";
|
||||
button.appendChild(img);
|
||||
return button;
|
||||
}
|
||||
|
||||
_loadTours(tours) {
|
||||
|
||||
@@ -10,6 +10,9 @@ onboarding.button.learnMore=Learn More
|
||||
onboarding.notification-icon-tool-tip=New to %S?
|
||||
# LOCALIZATION NOTE(onboarding.overlay-icon-tooltip): This string will be used to show the tooltip alongside the notification icon in the overlay tour. %S is brandShortName.
|
||||
onboarding.overlay-icon-tooltip=New to %S? Let’s get started.
|
||||
# LOCALIZATION NOTE(onboarding.overlay-icon-tooltip-updated): %S is brandShortName.
|
||||
onboarding.overlay-icon-tooltip-updated=%S is all new. See what you can do!
|
||||
onboarding.notification-icon-tooltip-updated=See what’s new!
|
||||
|
||||
onboarding.tour-search2=Search
|
||||
onboarding.tour-search.title2=Find it faster.
|
||||
@@ -95,3 +98,19 @@ onboarding.tour-library.button=Show Library in Menu
|
||||
onboarding.notification.onboarding-tour-library.title=Keep it together.
|
||||
# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-library.message): This string will be used in the notification message for the library tour. %S is brandShortName
|
||||
onboarding.notification.onboarding-tour-library.message=The new %S library puts the great things you’ve discovered on the web in one convenient place.
|
||||
|
||||
onboarding.tour-singlesearch=Address Bar
|
||||
onboarding.tour-singlesearch.title=Find it faster.
|
||||
# LOCALIZATION NOTE(onboarding.tour-singlesearch.description): %S is brandShortName
|
||||
onboarding.tour-singlesearch.description=The address bar might be the most powerful tool in the sleek new %S toolbar. Start typing, and see suggestions based on your browsing and search history. Go to a web address, search the whole web with your default search engine, or send your query directly to a single site with one-click search.
|
||||
onboarding.tour-singlesearch.button=Show Address Bar
|
||||
onboarding.notification.onboarding-tour-singlesearch.title=Find it faster.
|
||||
onboarding.notification.onboarding-tour-singlesearch.message=The unified address bar is the only tool you need to find your way around the web.
|
||||
|
||||
onboarding.tour-performance=Performance
|
||||
onboarding.tour-performance.title=Browse with the best of ‘em.
|
||||
# LOCALIZATION NOTE(onboarding.tour-performance.description): %1$S is brandShortName.
|
||||
onboarding.tour-performance.description=It’s a whole new %1$S, built for faster page loading, smoother scrolling, and more responsive tab switching. These performance upgrades come paired with a modern, intuitive design. Start browsing and experience it for yourself: the best %1$S yet.
|
||||
onboarding.notification.onboarding-tour-performance.title=Browse with the best of ‘em.
|
||||
# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-performance.message): %S is brandShortName.
|
||||
onboarding.notification.onboarding-tour-performance.message=Prepare yourself for the fastest, smoothest, most reliable %S yet.
|
||||
|
||||
@@ -7,7 +7,7 @@ function assertOnboardingDestroyed(browser) {
|
||||
return ContentTask.spawn(browser, {}, function() {
|
||||
let expectedRemovals = [
|
||||
"#onboarding-overlay",
|
||||
"#onboarding-overlay-icon"
|
||||
"#onboarding-overlay-button"
|
||||
];
|
||||
for (let selector of expectedRemovals) {
|
||||
let removal = content.document.querySelector(selector);
|
||||
@@ -36,7 +36,7 @@ add_task(async function test_hide_onboarding_tours() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
|
||||
tabs.push(tab);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ add_task(async function test_click_action_button_to_set_tour_completed() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
|
||||
tabs.push(tab);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ add_task(async function test_set_right_tour_completed_style_on_overlay() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
|
||||
tabs.push(tab);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ add_task(async function test_onboarding_default_new_tourset() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
|
||||
tabs.push(tab);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ add_task(async function test_onboarding_custom_new_tourset() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
|
||||
tabs.push(tab);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ add_task(async function test_onboarding_custom_update_tourset() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
|
||||
tabs.push(tab);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
:root {
|
||||
%ifdef MOZ_PHOTON_THEME
|
||||
--toolbarbutton-border-radius: 2px;
|
||||
--toolbarbutton-border-radius: 4px;
|
||||
%else
|
||||
--toolbarbutton-border-radius: 1px;
|
||||
|
||||
|
||||
@@ -1570,7 +1570,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||
}
|
||||
|
||||
%ifdef MOZ_PHOTON_THEME
|
||||
:root:-moz-any([inFullscreen], [tabsintitlebar]):not([customizing]) #TabsToolbar:not(:-moz-lwtheme) {
|
||||
:root:-moz-any([inFullscreen], [tabsintitlebar]) #TabsToolbar:not(:-moz-lwtheme) {
|
||||
-moz-appearance: none;
|
||||
background-color: #232323;
|
||||
color: hsl(240, 9%, 98%);
|
||||
|
||||
@@ -580,9 +580,7 @@ toolbarpaletteitem[place=toolbar] > toolbarspring {
|
||||
#customization-panelWrapper > .panel-arrowbox {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
%ifndef XP_MACOSX
|
||||
margin-bottom: 2px;
|
||||
%endif
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
#customization-panelWrapper > .panel-arrowbox > .panel-arrow[side="top"] {
|
||||
@@ -614,6 +612,7 @@ toolbarpaletteitem[place=toolbar] > toolbarspring {
|
||||
* 10px.
|
||||
*/
|
||||
margin-inline-end: calc(4px + 3 * var(--toolbarbutton-inner-padding));
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
%ifdef XP_MACOSX
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 49 KiB |
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 52 KiB |
@@ -47,7 +47,7 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: translateX(-738px);
|
||||
transform: translateX(-450px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
transform: scaleX(-1) translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: scaleX(-1) translateX(-738px);
|
||||
transform: scaleX(-1) translateX(-450px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: translateX(-612px);
|
||||
transform: translateX(-450px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,15 +74,20 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
transform: scaleX(-1) translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: scaleX(-1) translateX(-612px);
|
||||
transform: scaleX(-1) translateX(-450px);
|
||||
}
|
||||
}
|
||||
|
||||
#stop-reload-button[animate] > #reload-button,
|
||||
#stop-reload-button[animate] > #stop-button {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#reload-button > .toolbarbutton-animatable-box,
|
||||
#stop-button > .toolbarbutton-animatable-box {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
margin-top: -10px; /* Vertically center the 20px tall animatable image */
|
||||
top: calc(50% - 10px); /* Vertically center the 20px tall animatable image */
|
||||
/* Since .toolbarbutton-icon uses a different width than the animatable-box,
|
||||
we need to set a padding relative to the difference in widths. */
|
||||
margin-inline-start: calc((16px + 2 * var(--toolbarbutton-inner-padding) - 18px) / 2);
|
||||
@@ -98,7 +103,7 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
|
||||
#reload-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image,
|
||||
#stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
height: 20px; /* Height of each frame within the SVG sprite */
|
||||
height: var(--toolbarbutton-height); /* Height must be equal to height of toolbarbutton padding-box */
|
||||
animation-fill-mode: forwards;
|
||||
animation-iteration-count: 1;
|
||||
list-style-image: none;
|
||||
@@ -106,7 +111,7 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
|
||||
#stop-reload-button[animate] > #reload-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
background-image: url("chrome://browser/skin/reload-to-stop.svg");
|
||||
width: 756px;
|
||||
width: 468px;
|
||||
}
|
||||
|
||||
#stop-reload-button[animate] > #reload-button:not([displaystop]) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
@@ -118,13 +123,13 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
}
|
||||
|
||||
#reload-button:not([displaystop]) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
animation-timing-function: steps(41);
|
||||
animation-duration: 684ms;
|
||||
animation-timing-function: steps(25);
|
||||
animation-duration: 400ms;
|
||||
}
|
||||
|
||||
#stop-reload-button[animate] > #reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
background-image: url("chrome://browser/skin/stop-to-reload.svg");
|
||||
width: 630px;
|
||||
width: 468px;
|
||||
}
|
||||
|
||||
#stop-reload-button[animate] > #reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
@@ -136,24 +141,24 @@ toolbar[brighttext] .toolbarbutton-1 {
|
||||
}
|
||||
|
||||
#reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
animation-timing-function: steps(34);
|
||||
animation-duration: 600ms;
|
||||
animation-timing-function: steps(25);
|
||||
animation-duration: 400ms;
|
||||
}
|
||||
|
||||
#reload-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
transform: translateX(-738px);
|
||||
transform: translateX(-450px);
|
||||
}
|
||||
|
||||
#reload-button:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
transform: scaleX(-1) translateX(-738px);
|
||||
transform: scaleX(-1) translateX(-450px);
|
||||
}
|
||||
|
||||
#reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
transform: translateX(-612px);
|
||||
transform: translateX(-450px);
|
||||
}
|
||||
|
||||
#reload-button[displaystop] + #stop-button:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
|
||||
transform: scaleX(-1) translateX(-612px);
|
||||
transform: scaleX(-1) translateX(-450px);
|
||||
}
|
||||
%endif
|
||||
#reload-button {
|
||||
|
||||
@@ -235,10 +235,16 @@ button {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
#addons-panel h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.addon-target-container {
|
||||
background: #fff;
|
||||
box-shadow: 0 0 1px rgba(0, 0, 0, 0.12);
|
||||
list-style-type: none;
|
||||
font-size: 13px;
|
||||
margin: 0 0 8px;
|
||||
padding: 4px 16px;
|
||||
transition: box-shadow 150ms;
|
||||
@@ -253,7 +259,11 @@ button {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 16px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.addon-target-name {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.addon-target-actions {
|
||||
@@ -296,7 +306,7 @@ button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #0087ff;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
margin: 12px;
|
||||
min-width: auto;
|
||||
padding: 4px;
|
||||
|
||||
@@ -173,7 +173,9 @@ module.exports = createClass({
|
||||
role: "presentation",
|
||||
src: target.icon
|
||||
}),
|
||||
dom.span({ className: "target-name", title: target.name }, target.name)
|
||||
dom.span(
|
||||
{ className: "target-name addon-target-name", title: target.name },
|
||||
target.name)
|
||||
),
|
||||
showMessages(target),
|
||||
dom.dl(
|
||||
|
||||
@@ -78,6 +78,7 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
|
||||
[browser_inspector_highlighter-cssshape_01.js]
|
||||
[browser_inspector_highlighter-cssshape_02.js]
|
||||
[browser_inspector_highlighter-cssshape_03.js]
|
||||
[browser_inspector_highlighter-cssshape_04.js]
|
||||
[browser_inspector_highlighter-csstransform_01.js]
|
||||
[browser_inspector_highlighter-csstransform_02.js]
|
||||
[browser_inspector_highlighter-embed.js]
|
||||
|
||||
@@ -74,7 +74,7 @@ function* ellipseHasCorrectAttrs(testActor, inspector, highlighterFront) {
|
||||
is(rx, 40, "Ellipse highlighter has correct rx");
|
||||
is(ry, 30, "Ellipse highlighter has correct ry");
|
||||
is(cx, 25, "Ellipse highlighter has correct cx");
|
||||
is(cy, 75, "Ellipse highlighter has correct cy");
|
||||
is(cy, 30, "Ellipse highlighter has correct cy");
|
||||
}
|
||||
|
||||
function* insetHasCorrectAttrs(testActor, inspector, highlighterFront) {
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that shapes are updated correctly on mouse events.
|
||||
|
||||
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
|
||||
const HIGHLIGHTER_TYPE = "ShapesHighlighter";
|
||||
|
||||
add_task(function* () {
|
||||
let inspector = yield openInspectorForURL(TEST_URL);
|
||||
let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
|
||||
let {testActor} = inspector;
|
||||
|
||||
yield testPolygonMovePoint(testActor, helper);
|
||||
yield testPolygonAddPoint(testActor, helper);
|
||||
yield testPolygonRemovePoint(testActor, helper);
|
||||
yield testCircleMoveCenter(testActor, helper);
|
||||
yield testEllipseMoveRadius(testActor, helper);
|
||||
yield testInsetMoveEdges(testActor, helper);
|
||||
|
||||
helper.finalize();
|
||||
});
|
||||
|
||||
function* testPolygonMovePoint(testActor, helper) {
|
||||
info("Displaying polygon");
|
||||
yield helper.show("#polygon", {mode: "cssClipPath"});
|
||||
let { mouse, highlightedNode } = helper;
|
||||
|
||||
let points = yield helper.getElementAttribute("shapes-polygon", "points");
|
||||
let [x, y] = points.split(" ")[0].split(",");
|
||||
let quads = yield testActor.getAllAdjustedQuads("#polygon");
|
||||
let { top, left, width, height } = quads.border[0].bounds;
|
||||
x = left + width * x / 100;
|
||||
y = top + height * y / 100;
|
||||
let dx = width / 10;
|
||||
let dy = height / 10;
|
||||
|
||||
info("Moving first polygon point");
|
||||
yield mouse.down(x, y);
|
||||
yield mouse.move(x + dx, y + dy);
|
||||
yield mouse.up();
|
||||
yield testActor.reflow();
|
||||
|
||||
let computedStyle = yield highlightedNode.getComputedStyle();
|
||||
let definition = computedStyle["clip-path"].value;
|
||||
ok(definition.includes(`${dx}px ${dy}px`), `Point moved to ${dx}px ${dy}px`);
|
||||
}
|
||||
|
||||
function* testPolygonAddPoint(testActor, helper) {
|
||||
yield helper.show("#polygon", {mode: "cssClipPath"});
|
||||
let { mouse, highlightedNode } = helper;
|
||||
|
||||
// Move first point to have same x as second point, then double click between
|
||||
// the two points to add a new one.
|
||||
let points = yield helper.getElementAttribute("shapes-polygon", "points");
|
||||
let pointsArray = points.split(" ");
|
||||
let quads = yield testActor.getAllAdjustedQuads("#polygon");
|
||||
let { top, left, width, height } = quads.border[0].bounds;
|
||||
let [x1, y1] = pointsArray[0].split(",");
|
||||
let [x2, y2] = pointsArray[1].split(",");
|
||||
x1 = left + width * x1 / 100;
|
||||
x2 = left + width * x2 / 100;
|
||||
y1 = top + height * y1 / 100;
|
||||
y2 = top + height * y2 / 100;
|
||||
|
||||
yield mouse.down(x1, y1);
|
||||
yield mouse.move(x2, y1);
|
||||
yield mouse.up();
|
||||
yield testActor.reflow();
|
||||
|
||||
let newPointX = x2;
|
||||
let newPointY = (y1 + y2) / 2;
|
||||
let options = {
|
||||
selector: ":root",
|
||||
x: newPointX,
|
||||
y: newPointY,
|
||||
center: false,
|
||||
options: {clickCount: 2}
|
||||
};
|
||||
|
||||
info("Adding new polygon point");
|
||||
yield testActor.synthesizeMouse(options);
|
||||
yield testActor.reflow();
|
||||
|
||||
let computedStyle = yield highlightedNode.getComputedStyle();
|
||||
let definition = computedStyle["clip-path"].value;
|
||||
ok(definition.includes(`${newPointX * 100 / width}% ${newPointY * 100 / height}%`),
|
||||
"Point successfuly added");
|
||||
}
|
||||
|
||||
function* testPolygonRemovePoint(testActor, helper) {
|
||||
yield helper.show("#polygon", {mode: "cssClipPath"});
|
||||
let { highlightedNode } = helper;
|
||||
|
||||
let points = yield helper.getElementAttribute("shapes-polygon", "points");
|
||||
let [x, y] = points.split(" ")[0].split(",");
|
||||
let quads = yield testActor.getAllAdjustedQuads("#polygon");
|
||||
let { top, left, width, height } = quads.border[0].bounds;
|
||||
|
||||
let options = {
|
||||
selector: ":root",
|
||||
x: left + width * x / 100,
|
||||
y: top + height * y / 100,
|
||||
center: false,
|
||||
options: {clickCount: 2}
|
||||
};
|
||||
|
||||
info("Removing first polygon point");
|
||||
yield testActor.synthesizeMouse(options);
|
||||
yield testActor.reflow();
|
||||
|
||||
let computedStyle = yield highlightedNode.getComputedStyle();
|
||||
let definition = computedStyle["clip-path"].value;
|
||||
ok(!definition.includes(`${x}% ${y}%`), "Point successfully removed");
|
||||
}
|
||||
|
||||
function* testCircleMoveCenter(testActor, helper) {
|
||||
yield helper.show("#circle", {mode: "cssClipPath"});
|
||||
let { mouse, highlightedNode } = helper;
|
||||
|
||||
let cx = parseFloat(yield helper.getElementAttribute("shapes-ellipse", "cx"));
|
||||
let cy = parseFloat(yield helper.getElementAttribute("shapes-ellipse", "cy"));
|
||||
let quads = yield testActor.getAllAdjustedQuads("#circle");
|
||||
let { width, height } = quads.border[0].bounds;
|
||||
let cxPixel = width * cx / 100;
|
||||
let cyPixel = height * cy / 100;
|
||||
let dx = width / 10;
|
||||
let dy = height / 10;
|
||||
|
||||
info("Moving circle center");
|
||||
yield mouse.down(cxPixel, cyPixel, "#circle");
|
||||
yield mouse.move(cxPixel + dx, cyPixel + dy, "#circle");
|
||||
yield mouse.up(cxPixel + dx, cyPixel + dy, "#circle");
|
||||
yield testActor.reflow();
|
||||
|
||||
let computedStyle = yield highlightedNode.getComputedStyle();
|
||||
let definition = computedStyle["clip-path"].value;
|
||||
ok(definition.includes(`at ${cx + 10}% ${cy + 10}%`),
|
||||
"Circle center successfully moved");
|
||||
}
|
||||
|
||||
function* testEllipseMoveRadius(testActor, helper) {
|
||||
yield helper.show("#ellipse", {mode: "cssClipPath"});
|
||||
let { mouse, highlightedNode } = helper;
|
||||
|
||||
let rx = parseFloat(yield helper.getElementAttribute("shapes-ellipse", "rx"));
|
||||
let ry = parseFloat(yield helper.getElementAttribute("shapes-ellipse", "ry"));
|
||||
let cx = parseFloat(yield helper.getElementAttribute("shapes-ellipse", "cx"));
|
||||
let cy = parseFloat(yield helper.getElementAttribute("shapes-ellipse", "cy"));
|
||||
let quads = yield testActor.getAllAdjustedQuads("#ellipse");
|
||||
let { width, height } = quads.content[0].bounds;
|
||||
let computedStyle = yield highlightedNode.getComputedStyle();
|
||||
let paddingTop = parseFloat(computedStyle["padding-top"].value);
|
||||
let paddingLeft = parseFloat(computedStyle["padding-left"].value);
|
||||
let cxPixel = paddingLeft + width * cx / 100;
|
||||
let cyPixel = paddingTop + height * cy / 100;
|
||||
let rxPixel = cxPixel + width * rx / 100;
|
||||
let ryPixel = cyPixel + height * ry / 100;
|
||||
let dx = width / 10;
|
||||
let dy = height / 10;
|
||||
|
||||
info("Moving ellipse rx");
|
||||
yield mouse.down(rxPixel, cyPixel, "#ellipse");
|
||||
yield mouse.move(rxPixel + dx, cyPixel, "#ellipse");
|
||||
yield mouse.up(rxPixel + dx, cyPixel, "#ellipse");
|
||||
yield testActor.reflow();
|
||||
|
||||
info("Moving ellipse ry");
|
||||
yield mouse.down(cxPixel, ryPixel, "#ellipse");
|
||||
yield mouse.move(cxPixel, ryPixel - dy, "#ellipse");
|
||||
yield mouse.up(cxPixel, ryPixel - dy, "#ellipse");
|
||||
yield testActor.reflow();
|
||||
|
||||
computedStyle = yield highlightedNode.getComputedStyle();
|
||||
let definition = computedStyle["clip-path"].value;
|
||||
ok(definition.includes(`${rx + 10}% ${ry - 10}%`),
|
||||
"Ellipse radiuses successfully moved");
|
||||
}
|
||||
|
||||
function* testInsetMoveEdges(testActor, helper) {
|
||||
yield helper.show("#inset", {mode: "cssClipPath"});
|
||||
let { mouse, highlightedNode } = helper;
|
||||
|
||||
let x = parseFloat(yield helper.getElementAttribute("shapes-rect", "x"));
|
||||
let y = parseFloat(yield helper.getElementAttribute("shapes-rect", "y"));
|
||||
let width = parseFloat(yield helper.getElementAttribute("shapes-rect", "width"));
|
||||
let height = parseFloat(yield helper.getElementAttribute("shapes-rect", "height"));
|
||||
let quads = yield testActor.getAllAdjustedQuads("#inset");
|
||||
let { width: elemWidth, height: elemHeight } = quads.content[0].bounds;
|
||||
|
||||
let left = elemWidth * x / 100;
|
||||
let top = elemHeight * y / 100;
|
||||
let right = left + elemWidth * width / 100;
|
||||
let bottom = top + elemHeight * height / 100;
|
||||
let xCenter = (left + right) / 2;
|
||||
let yCenter = (top + bottom) / 2;
|
||||
let dx = elemWidth / 10;
|
||||
let dy = elemHeight / 10;
|
||||
|
||||
info("Moving inset top");
|
||||
yield mouse.down(xCenter, top, "#inset");
|
||||
yield mouse.move(xCenter, top + dy, "#inset");
|
||||
yield mouse.up(xCenter, top + dy, "#inset");
|
||||
yield testActor.reflow();
|
||||
|
||||
info("Moving inset bottom");
|
||||
yield mouse.down(xCenter, bottom, "#inset");
|
||||
yield mouse.move(xCenter, bottom + dy, "#inset");
|
||||
yield mouse.up(xCenter, bottom + dy, "#inset");
|
||||
yield testActor.reflow();
|
||||
|
||||
info("Moving inset left");
|
||||
yield mouse.down(left, yCenter, "#inset");
|
||||
yield mouse.move(left + dx, yCenter, "#inset");
|
||||
yield mouse.up(left + dx, yCenter, "#inset");
|
||||
yield testActor.reflow();
|
||||
|
||||
info("Moving inset right");
|
||||
yield mouse.down(right, yCenter, "#inset");
|
||||
yield mouse.move(right + dx, yCenter, "#inset");
|
||||
yield mouse.up(right + dx, yCenter, "#inset");
|
||||
yield testActor.reflow();
|
||||
|
||||
let computedStyle = yield highlightedNode.getComputedStyle();
|
||||
let definition = computedStyle["clip-path"].value;
|
||||
ok(definition.includes(
|
||||
`${top + dy}px ${elemWidth - right - dx}px ${100 - y - height - 10}% ${x + 10}%`),
|
||||
"Inset edges successfully moved");
|
||||
}
|
||||
@@ -30,11 +30,11 @@
|
||||
clip-path: circle(25% at 30% 40%);
|
||||
}
|
||||
#ellipse {
|
||||
clip-path: ellipse(40% 30% at 25% 75%) content-box;
|
||||
clip-path: ellipse(40% 30% at 25% 30%) content-box;
|
||||
padding: 20px;
|
||||
}
|
||||
#ellipse-padding-box {
|
||||
clip-path: ellipse(40% 30% at 25% 75%) padding-box;
|
||||
clip-path: ellipse(40% 30% at 25% 30%) padding-box;
|
||||
padding: 20px;
|
||||
}
|
||||
#inset {
|
||||
|
||||
@@ -503,11 +503,11 @@ const getHighlighterHelperFor = (type) => Task.async(
|
||||
// mouse.up(); // synthesize "mouseup" at 20,30
|
||||
mouse: new Proxy({}, {
|
||||
get: (target, name) =>
|
||||
function* (x = prevX, y = prevY) {
|
||||
function* (x = prevX, y = prevY, selector = ":root") {
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
yield testActor.synthesizeMouse({
|
||||
selector: ":root", x, y, options: {type: "mouse" + name}});
|
||||
selector, x, y, options: {type: "mouse" + name}});
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
@@ -628,6 +628,14 @@ netmonitor.toolbar.filterFreetext.label=Filter URLs
|
||||
# shortcut key to focus on the toolbar url filtering textbox
|
||||
netmonitor.toolbar.filterFreetext.key=CmdOrCtrl+F
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.toolbar.disableCache.label): This is the label
|
||||
# displayed for the checkbox for disabling browser cache.
|
||||
netmonitor.toolbar.disableCache.label=Disable cache
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.toolbar.disableCache.tooltip): This is the tooltip
|
||||
# displayed for the checkbox for disabling browser cache.
|
||||
netmonitor.toolbar.disableCache.tooltip=Disable HTTP cache
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.toolbar.clear): This is the label displayed
|
||||
# in the network toolbar for the "Clear" button.
|
||||
netmonitor.toolbar.clear=Clear
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
const {
|
||||
ACTIVITY_TYPE,
|
||||
OPEN_NETWORK_DETAILS,
|
||||
DISABLE_BROWSER_CACHE,
|
||||
OPEN_STATISTICS,
|
||||
RESET_COLUMNS,
|
||||
SELECT_DETAILS_PANEL_TAB,
|
||||
@@ -27,6 +28,18 @@ function openNetworkDetails(open) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Change browser cache state.
|
||||
*
|
||||
* @param {boolean} disabled - expected browser cache in disable state
|
||||
*/
|
||||
function disableBrowserCache(disabled) {
|
||||
return {
|
||||
type: DISABLE_BROWSER_CACHE,
|
||||
disabled,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Change performance statistics panel open state.
|
||||
*
|
||||
@@ -94,6 +107,14 @@ function toggleNetworkDetails() {
|
||||
dispatch(openNetworkDetails(!getState().ui.networkDetailsOpen));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle browser cache status.
|
||||
*/
|
||||
function toggleBrowserCache() {
|
||||
return (dispatch, getState) =>
|
||||
dispatch(disableBrowserCache(!getState().ui.browserCacheDisabled));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle performance statistics panel.
|
||||
*/
|
||||
@@ -104,11 +125,13 @@ function toggleStatistics() {
|
||||
|
||||
module.exports = {
|
||||
openNetworkDetails,
|
||||
disableBrowserCache,
|
||||
openStatistics,
|
||||
resetColumns,
|
||||
resizeWaterfall,
|
||||
selectDetailsPanelTab,
|
||||
toggleColumn,
|
||||
toggleNetworkDetails,
|
||||
toggleBrowserCache,
|
||||
toggleStatistics,
|
||||
};
|
||||
|
||||
@@ -795,6 +795,17 @@ body,
|
||||
margin: 1px 3px;
|
||||
}
|
||||
|
||||
.devtools-checkbox {
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
bottom: 1px;
|
||||
}
|
||||
|
||||
.devtools-checkbox-label {
|
||||
margin-inline-start: 10px;
|
||||
margin-inline-end: 3px;
|
||||
}
|
||||
|
||||
/* Empty notices in tab panels */
|
||||
|
||||
.empty-notice {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
const {
|
||||
createClass,
|
||||
createFactory,
|
||||
@@ -25,7 +26,7 @@ const { L10N } = require("../utils/l10n");
|
||||
// Components
|
||||
const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
|
||||
|
||||
const { button, div, span } = DOM;
|
||||
const { button, div, input, label, span } = DOM;
|
||||
|
||||
const COLLPASE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
|
||||
const EXPAND_DETAILS_PANE = L10N.getStr("expandDetailsPane");
|
||||
@@ -33,6 +34,8 @@ const SEARCH_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.filterFreetext.key")
|
||||
const SEARCH_PLACE_HOLDER = L10N.getStr("netmonitor.toolbar.filterFreetext.label");
|
||||
const TOOLBAR_CLEAR = L10N.getStr("netmonitor.toolbar.clear");
|
||||
|
||||
const DEVTOOLS_DISABLE_CACHE_PREF = "devtools.cache.disabled";
|
||||
|
||||
/*
|
||||
* Network monitor toolbar component
|
||||
* Toolbar contains a set of useful tools to control network requests
|
||||
@@ -47,6 +50,9 @@ const Toolbar = createClass({
|
||||
networkDetailsToggleDisabled: PropTypes.bool.isRequired,
|
||||
networkDetailsOpen: PropTypes.bool.isRequired,
|
||||
toggleNetworkDetails: PropTypes.func.isRequired,
|
||||
disableBrowserCache: PropTypes.func.isRequired,
|
||||
toggleBrowserCache: PropTypes.func.isRequired,
|
||||
browserCacheDisabled: PropTypes.bool.isRequired,
|
||||
toggleRequestFilterType: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
@@ -65,6 +71,8 @@ const Toolbar = createClass({
|
||||
networkDetailsToggleDisabled,
|
||||
networkDetailsOpen,
|
||||
toggleNetworkDetails,
|
||||
toggleBrowserCache,
|
||||
browserCacheDisabled,
|
||||
} = this.props;
|
||||
|
||||
let toggleButtonClassName = [
|
||||
@@ -102,6 +110,20 @@ const Toolbar = createClass({
|
||||
onClick: clearRequests,
|
||||
}),
|
||||
div({ className: "requests-list-filter-buttons" }, buttons),
|
||||
label(
|
||||
{
|
||||
className: "devtools-checkbox-label",
|
||||
title: L10N.getStr("netmonitor.toolbar.disableCache.tooltip"),
|
||||
},
|
||||
input({
|
||||
id: "devtools-cache-checkbox",
|
||||
className: "devtools-checkbox",
|
||||
type: "checkbox",
|
||||
checked: browserCacheDisabled,
|
||||
onClick: toggleBrowserCache,
|
||||
}),
|
||||
L10N.getStr("netmonitor.toolbar.disableCache.label"),
|
||||
),
|
||||
),
|
||||
span({ className: "devtools-toolbar-group" },
|
||||
SearchBox({
|
||||
@@ -122,6 +144,21 @@ const Toolbar = createClass({
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
Services.prefs.addObserver(DEVTOOLS_DISABLE_CACHE_PREF,
|
||||
this.updateBrowserCacheDisabled);
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
Services.prefs.removeObserver(DEVTOOLS_DISABLE_CACHE_PREF,
|
||||
this.updateBrowserCacheDisabled);
|
||||
},
|
||||
|
||||
updateBrowserCacheDisabled() {
|
||||
this.props.disableBrowserCache(
|
||||
Services.prefs.getBoolPref(DEVTOOLS_DISABLE_CACHE_PREF));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -129,6 +166,7 @@ module.exports = connect(
|
||||
(state) => ({
|
||||
networkDetailsToggleDisabled: isNetworkDetailsToggleButtonDisabled(state),
|
||||
networkDetailsOpen: state.ui.networkDetailsOpen,
|
||||
browserCacheDisabled: state.ui.browserCacheDisabled,
|
||||
requestFilterTypes: getRequestFilterTypes(state),
|
||||
summary: getDisplayedRequestsSummary(state),
|
||||
}),
|
||||
@@ -137,5 +175,7 @@ module.exports = connect(
|
||||
setRequestFilterText: (text) => dispatch(Actions.setRequestFilterText(text)),
|
||||
toggleRequestFilterType: (type) => dispatch(Actions.toggleRequestFilterType(type)),
|
||||
toggleNetworkDetails: () => dispatch(Actions.toggleNetworkDetails()),
|
||||
disableBrowserCache: (disabled) => dispatch(Actions.disableBrowserCache(disabled)),
|
||||
toggleBrowserCache: () => dispatch(Actions.toggleBrowserCache()),
|
||||
}),
|
||||
)(Toolbar);
|
||||
|
||||
@@ -14,6 +14,7 @@ const actionTypes = {
|
||||
CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
|
||||
ENABLE_REQUEST_FILTER_TYPE_ONLY: "ENABLE_REQUEST_FILTER_TYPE_ONLY",
|
||||
OPEN_NETWORK_DETAILS: "OPEN_NETWORK_DETAILS",
|
||||
DISABLE_BROWSER_CACHE: "DISABLE_BROWSER_CACHE",
|
||||
OPEN_STATISTICS: "OPEN_STATISTICS",
|
||||
REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
|
||||
RESET_COLUMNS: "RESET_COLUMNS",
|
||||
|
||||
@@ -10,6 +10,7 @@ const {
|
||||
RESET_COLUMNS,
|
||||
TOGGLE_COLUMN,
|
||||
TOGGLE_REQUEST_FILTER_TYPE,
|
||||
DISABLE_BROWSER_CACHE,
|
||||
} = require("../constants");
|
||||
const { getRequestFilterTypes } = require("../selectors/index");
|
||||
|
||||
@@ -30,6 +31,10 @@ function prefsMiddleware(store) {
|
||||
Services.prefs.setCharPref(
|
||||
"devtools.netmonitor.filters", JSON.stringify(filters));
|
||||
break;
|
||||
case DISABLE_BROWSER_CACHE:
|
||||
Services.prefs.setBoolPref(
|
||||
"devtools.cache.disabled", store.getState().ui.browserCacheDisabled);
|
||||
break;
|
||||
case TOGGLE_COLUMN:
|
||||
case RESET_COLUMNS:
|
||||
let visibleColumns = [...store.getState().ui.columns]
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
"use strict";
|
||||
|
||||
const I = require("devtools/client/shared/vendor/immutable");
|
||||
const Services = require("Services");
|
||||
const {
|
||||
CLEAR_REQUESTS,
|
||||
OPEN_NETWORK_DETAILS,
|
||||
DISABLE_BROWSER_CACHE,
|
||||
OPEN_STATISTICS,
|
||||
REMOVE_SELECTED_CUSTOM_REQUEST,
|
||||
RESET_COLUMNS,
|
||||
@@ -51,6 +53,7 @@ const UI = I.Record({
|
||||
columns: new Columns(),
|
||||
detailsPanelSelectedTab: "headers",
|
||||
networkDetailsOpen: false,
|
||||
browserCacheDisabled: Services.prefs.getBoolPref("devtools.cache.disabled"),
|
||||
statisticsOpen: false,
|
||||
waterfallWidth: null,
|
||||
});
|
||||
@@ -67,6 +70,10 @@ function openNetworkDetails(state, action) {
|
||||
return state.set("networkDetailsOpen", action.open);
|
||||
}
|
||||
|
||||
function disableBrowserCache(state, action) {
|
||||
return state.set("browserCacheDisabled", action.disabled);
|
||||
}
|
||||
|
||||
function openStatistics(state, action) {
|
||||
return state.set("statisticsOpen", action.open);
|
||||
}
|
||||
@@ -94,6 +101,8 @@ function ui(state = new UI(), action) {
|
||||
return openNetworkDetails(state, { open: false });
|
||||
case OPEN_NETWORK_DETAILS:
|
||||
return openNetworkDetails(state, action);
|
||||
case DISABLE_BROWSER_CACHE:
|
||||
return disableBrowserCache(state, action);
|
||||
case OPEN_STATISTICS:
|
||||
return openStatistics(state, action);
|
||||
case RESET_COLUMNS:
|
||||
|
||||
@@ -70,7 +70,7 @@ const MessageContainer = createClass({
|
||||
const message = this.props.getMessage();
|
||||
|
||||
let MessageComponent = getMessageComponent(message);
|
||||
return MessageComponent(Object.assign({message, indent: message.indent}, this.props));
|
||||
return MessageComponent(Object.assign({message}, this.props));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -25,14 +25,12 @@ ConsoleApiCall.propTypes = {
|
||||
message: PropTypes.object.isRequired,
|
||||
open: PropTypes.bool,
|
||||
serviceContainer: PropTypes.object.isRequired,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
loadedObjectProperties: PropTypes.object,
|
||||
};
|
||||
|
||||
ConsoleApiCall.defaultProps = {
|
||||
open: false,
|
||||
indent: 0,
|
||||
};
|
||||
|
||||
function ConsoleApiCall(props) {
|
||||
@@ -42,13 +40,13 @@ function ConsoleApiCall(props) {
|
||||
open,
|
||||
tableData,
|
||||
serviceContainer,
|
||||
indent,
|
||||
timestampsVisible,
|
||||
repeat,
|
||||
loadedObjectProperties,
|
||||
} = props;
|
||||
const {
|
||||
id: messageId,
|
||||
indent,
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
|
||||
@@ -17,27 +17,22 @@ ConsoleCommand.displayName = "ConsoleCommand";
|
||||
|
||||
ConsoleCommand.propTypes = {
|
||||
message: PropTypes.object.isRequired,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
serviceContainer: PropTypes.object,
|
||||
};
|
||||
|
||||
ConsoleCommand.defaultProps = {
|
||||
indent: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays input from the console.
|
||||
*/
|
||||
function ConsoleCommand(props) {
|
||||
const {
|
||||
indent,
|
||||
message,
|
||||
timestampsVisible,
|
||||
serviceContainer,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
indent,
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
|
||||
@@ -19,22 +19,16 @@ EvaluationResult.displayName = "EvaluationResult";
|
||||
EvaluationResult.propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
message: PropTypes.object.isRequired,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
serviceContainer: PropTypes.object,
|
||||
loadedObjectProperties: PropTypes.object,
|
||||
};
|
||||
|
||||
EvaluationResult.defaultProps = {
|
||||
indent: 0,
|
||||
};
|
||||
|
||||
function EvaluationResult(props) {
|
||||
const {
|
||||
dispatch,
|
||||
message,
|
||||
serviceContainer,
|
||||
indent,
|
||||
timestampsVisible,
|
||||
loadedObjectProperties,
|
||||
} = props;
|
||||
@@ -45,6 +39,7 @@ function EvaluationResult(props) {
|
||||
helperType,
|
||||
level,
|
||||
id: messageId,
|
||||
indent,
|
||||
exceptionDocURL,
|
||||
frame,
|
||||
timeStamp,
|
||||
|
||||
@@ -22,17 +22,11 @@ NetworkEventMessage.propTypes = {
|
||||
serviceContainer: PropTypes.shape({
|
||||
openNetworkPanel: PropTypes.func.isRequired,
|
||||
}),
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
networkMessageUpdate: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
NetworkEventMessage.defaultProps = {
|
||||
indent: 0,
|
||||
};
|
||||
|
||||
function NetworkEventMessage({
|
||||
indent,
|
||||
message = {},
|
||||
serviceContainer,
|
||||
timestampsVisible,
|
||||
@@ -40,6 +34,7 @@ function NetworkEventMessage({
|
||||
}) {
|
||||
const {
|
||||
actor,
|
||||
indent,
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
|
||||
@@ -18,14 +18,12 @@ PageError.displayName = "PageError";
|
||||
PageError.propTypes = {
|
||||
message: PropTypes.object.isRequired,
|
||||
open: PropTypes.bool,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
serviceContainer: PropTypes.object,
|
||||
};
|
||||
|
||||
PageError.defaultProps = {
|
||||
open: false,
|
||||
indent: 0,
|
||||
};
|
||||
|
||||
function PageError(props) {
|
||||
@@ -34,11 +32,11 @@ function PageError(props) {
|
||||
message,
|
||||
open,
|
||||
serviceContainer,
|
||||
indent,
|
||||
timestampsVisible,
|
||||
} = props;
|
||||
const {
|
||||
id: messageId,
|
||||
indent,
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
|
||||
@@ -86,7 +86,10 @@ describe("ConsoleAPICall component:", () => {
|
||||
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
|
||||
|
||||
const indent = 10;
|
||||
let wrapper = render(ConsoleApiCall({ message, serviceContainer, indent }));
|
||||
let wrapper = render(ConsoleApiCall({
|
||||
message: Object.assign({}, message, {indent}),
|
||||
serviceContainer
|
||||
}));
|
||||
expect(wrapper.find(".indent").prop("style").width)
|
||||
.toBe(`${indent * INDENT_WIDTH}px`);
|
||||
|
||||
|
||||
@@ -81,7 +81,9 @@ describe("EvaluationResult component:", () => {
|
||||
const message = stubPreparedMessages.get("new Date(0)");
|
||||
|
||||
const indent = 10;
|
||||
let wrapper = render(EvaluationResult({ message, indent}));
|
||||
let wrapper = render(EvaluationResult({
|
||||
message: Object.assign({}, message, {indent}),
|
||||
}));
|
||||
expect(wrapper.find(".indent").prop("style").width)
|
||||
.toBe(`${indent * INDENT_WIDTH}px`);
|
||||
|
||||
|
||||
@@ -57,7 +57,10 @@ describe("NetworkEventMessage component:", () => {
|
||||
const message = stubPreparedMessages.get("GET request");
|
||||
|
||||
const indent = 10;
|
||||
let wrapper = render(NetworkEventMessage({ message, serviceContainer, indent}));
|
||||
let wrapper = render(NetworkEventMessage({
|
||||
message: Object.assign({}, message, {indent}),
|
||||
serviceContainer
|
||||
}));
|
||||
expect(wrapper.find(".indent").prop("style").width)
|
||||
.toBe(`${indent * INDENT_WIDTH}px`);
|
||||
|
||||
|
||||
@@ -142,7 +142,10 @@ describe("PageError component:", () => {
|
||||
it("has the expected indent", () => {
|
||||
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
|
||||
const indent = 10;
|
||||
let wrapper = render(PageError({ message, serviceContainer, indent}));
|
||||
let wrapper = render(PageError({
|
||||
message: Object.assign({}, message, {indent}),
|
||||
serviceContainer
|
||||
}));
|
||||
expect(wrapper.find(".indent").prop("style").width)
|
||||
.toBe(`${indent * INDENT_WIDTH}px`);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ DevToolsModules(
|
||||
'css-grid-utils.js',
|
||||
'make-debugger.js',
|
||||
'map-uri-to-addon-id.js',
|
||||
'shapes-geometry-utils.js',
|
||||
'stack.js',
|
||||
'TabSources.js',
|
||||
'walker-search.js',
|
||||
|
||||
110
devtools/server/actors/utils/shapes-geometry-utils.js
Normal file
110
devtools/server/actors/utils/shapes-geometry-utils.js
Normal file
@@ -0,0 +1,110 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Get the distance between two points on a plane.
|
||||
* @param {Number} x1 the x coord of the first point
|
||||
* @param {Number} y1 the y coord of the first point
|
||||
* @param {Number} x2 the x coord of the second point
|
||||
* @param {Number} y2 the y coord of the second point
|
||||
* @returns {Number} the distance between the two points
|
||||
*/
|
||||
const getDistance = (x1, y1, x2, y2) => {
|
||||
return Math.hypot(x2 - x1, y2 - y1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the given x/y coords are along the edge of the given ellipse.
|
||||
* We allow for a small area around the edge that still counts as being on the edge.
|
||||
* @param {Number} x the x coordinate of the click
|
||||
* @param {Number} y the y coordinate of the click
|
||||
* @param {Number} cx the x coordinate of the center of the ellipse
|
||||
* @param {Number} cy the y coordinate of the center of the ellipse
|
||||
* @param {Number} rx the x radius of the ellipse
|
||||
* @param {Number} ry the y radius of the ellipse
|
||||
* @param {Number} clickWidthX the width of the area that counts as being on the edge
|
||||
* along the x radius.
|
||||
* @param {Number} clickWidthY the width of the area that counts as being on the edge
|
||||
* along the y radius.
|
||||
* @returns {Boolean} whether the click counts as being on the edge of the ellipse.
|
||||
*/
|
||||
const clickedOnEllipseEdge = (x, y, cx, cy, rx, ry, clickWidthX, clickWidthY) => {
|
||||
// The formula to determine if something is inside or on the edge of an ellipse is:
|
||||
// (x - cx)^2/rx^2 + (y - cy)^2/ry^2 <= 1. If > 1, it's outside.
|
||||
// We make two ellipses, adjusting rx and ry with clickWidthX and clickWidthY
|
||||
// to allow for an area around the edge of the ellipse that can be clicked on.
|
||||
// If the click was outside the inner ellipse and inside the outer ellipse, return true.
|
||||
let inner = ((x - cx) ** 2) / (rx - clickWidthX) ** 2 +
|
||||
((y - cy) ** 2) / (ry - clickWidthY) ** 2;
|
||||
let outer = ((x - cx) ** 2) / (rx + clickWidthX) ** 2 +
|
||||
((y - cy) ** 2) / (ry + clickWidthY) ** 2;
|
||||
return inner >= 1 && outer <= 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the distance between a point and a line defined by two other points.
|
||||
* @param {Number} x1 the x coordinate of the first point in the line
|
||||
* @param {Number} y1 the y coordinate of the first point in the line
|
||||
* @param {Number} x2 the x coordinate of the second point in the line
|
||||
* @param {Number} y2 the y coordinate of the second point in the line
|
||||
* @param {Number} x3 the x coordinate of the point for which the distance is found
|
||||
* @param {Number} y3 the y coordinate of the point for which the distance is found
|
||||
* @returns {Number} the distance between (x3,y3) and the line defined by
|
||||
* (x1,y1) and (y1,y2)
|
||||
*/
|
||||
const distanceToLine = (x1, y1, x2, y2, x3, y3) => {
|
||||
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
|
||||
let num = Math.abs((y2 - y1) * x3 - (x2 - x1) * y3 + x2 * y1 - y2 * x1);
|
||||
let denom = getDistance(x1, y1, x2, y2);
|
||||
return num / denom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the point on the line defined by points a,b that is closest to point c
|
||||
* @param {Number} ax the x coordinate of point a
|
||||
* @param {Number} ay the y coordinate of point a
|
||||
* @param {Number} bx the x coordinate of point b
|
||||
* @param {Number} by the y coordinate of point b
|
||||
* @param {Number} cx the x coordinate of point c
|
||||
* @param {Number} cy the y coordinate of point c
|
||||
* @returns {Array} a 2 element array that contains the x/y coords of the projected point
|
||||
*/
|
||||
const projection = (ax, ay, bx, by, cx, cy) => {
|
||||
// https://en.wikipedia.org/wiki/Vector_projection#Vector_projection_2
|
||||
let ab = [bx - ax, by - ay];
|
||||
let ac = [cx - ax, cy - ay];
|
||||
let scalar = dotProduct(ab, ac) / dotProduct(ab, ab);
|
||||
return [ax + scalar * ab[0], ay + scalar * ab[1]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the dot product of two vectors, represented by arrays of numbers.
|
||||
* @param {Array} a the first vector
|
||||
* @param {Array} b the second vector
|
||||
* @returns {Number} the dot product of a and b
|
||||
*/
|
||||
const dotProduct = (a, b) => {
|
||||
return a.reduce((prev, curr, i) => {
|
||||
return prev + curr * b[i];
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the given x/y coords are above the given point.
|
||||
* @param {Number} x the x coordinate of the click
|
||||
* @param {Number} y the y coordinate of the click
|
||||
* @param {Number} pointX the x coordinate of the center of the point
|
||||
* @param {Number} pointY the y coordinate of the center of the point
|
||||
* @param {Number} radiusX the x radius of the point
|
||||
* @param {Number} radiusY the y radius of the point
|
||||
* @returns {Boolean} whether the click was on the point
|
||||
*/
|
||||
const clickedOnPoint = (x, y, pointX, pointY, radiusX, radiusY) => {
|
||||
return x >= pointX - radiusX && x <= pointX + radiusX &&
|
||||
y >= pointY - radiusY && y <= pointY + radiusY;
|
||||
};
|
||||
|
||||
exports.getDistance = getDistance;
|
||||
exports.clickedOnEllipseEdge = clickedOnEllipseEdge;
|
||||
exports.distanceToLine = distanceToLine;
|
||||
exports.projection = projection;
|
||||
exports.clickedOnPoint = clickedOnPoint;
|
||||
@@ -12,7 +12,8 @@ const {
|
||||
coordToPercent,
|
||||
evalCalcExpression,
|
||||
shapeModeToCssPropertyName,
|
||||
getCirclePath
|
||||
getCirclePath,
|
||||
getUnit
|
||||
} = require("devtools/server/actors/highlighters/shapes");
|
||||
|
||||
function run_test() {
|
||||
@@ -21,6 +22,7 @@ function run_test() {
|
||||
test_eval_calc_expression();
|
||||
test_shape_mode_to_css_property_name();
|
||||
test_get_circle_path();
|
||||
test_get_unit();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
@@ -32,7 +34,7 @@ function test_split_coords() {
|
||||
}, {
|
||||
desc: "splitCoords for coord pair with calc()",
|
||||
expr: "calc(50px + 20%) 30%",
|
||||
expected: ["calc(50px+20%)", "30%"]
|
||||
expected: ["calc(50px\u00a0+\u00a020%)", "30%"]
|
||||
}];
|
||||
|
||||
for (let { desc, expr, expected } of tests) {
|
||||
@@ -125,3 +127,41 @@ function test_get_circle_path() {
|
||||
equal(getCirclePath(cx, cy, width, height, zoom), expected, desc);
|
||||
}
|
||||
}
|
||||
|
||||
function test_get_unit() {
|
||||
const tests = [{
|
||||
desc: "getUnit with %",
|
||||
expr: "30%", expected: "%"
|
||||
}, {
|
||||
desc: "getUnit with px",
|
||||
expr: "400px", expected: "px"
|
||||
}, {
|
||||
desc: "getUnit with em",
|
||||
expr: "4em", expected: "em"
|
||||
}, {
|
||||
desc: "getUnit with 0",
|
||||
expr: "0", expected: "px"
|
||||
}, {
|
||||
desc: "getUnit with 0%",
|
||||
expr: "0%", expected: "px"
|
||||
}, {
|
||||
desc: "getUnit with no unit",
|
||||
expr: "30", expected: "px"
|
||||
}, {
|
||||
desc: "getUnit with calc",
|
||||
expr: "calc(30px + 5%)", expected: "px"
|
||||
}, {
|
||||
desc: "getUnit with var",
|
||||
expr: "var(--variable)", expected: "px"
|
||||
}, {
|
||||
desc: "getUnit with closest-side",
|
||||
expr: "closest-side", expected: "px"
|
||||
}, {
|
||||
desc: "getUnit with farthest-side",
|
||||
expr: "farthest-side", expected: "px"
|
||||
}];
|
||||
|
||||
for (let { desc, expr, expected } of tests) {
|
||||
equal(getUnit(expr), expected, desc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DevToolsShim",
|
||||
];
|
||||
@@ -37,11 +41,22 @@ this.DevToolsShim = {
|
||||
themes: [],
|
||||
|
||||
/**
|
||||
* Check if DevTools are currently installed and available.
|
||||
* Check if DevTools are currently installed (but not necessarily initialized).
|
||||
*
|
||||
* @return {Boolean} true if DevTools are installed.
|
||||
*/
|
||||
isInstalled: function () {
|
||||
return Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler)
|
||||
.hasSubstitution("devtools");
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if DevTools have already been initialized.
|
||||
*
|
||||
* @return {Boolean} true if DevTools are initialized.
|
||||
*/
|
||||
isInitialized: function () {
|
||||
return !!this.gDevTools;
|
||||
},
|
||||
|
||||
@@ -61,14 +76,14 @@ this.DevToolsShim = {
|
||||
* shutdown.
|
||||
*/
|
||||
unregister: function () {
|
||||
if (this.isInstalled()) {
|
||||
if (this.isInitialized()) {
|
||||
this.gDevTools.emit("devtools-unregistered");
|
||||
this.gDevTools = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The following methods can be called before DevTools are installed:
|
||||
* The following methods can be called before DevTools are initialized:
|
||||
* - on
|
||||
* - off
|
||||
* - registerTool
|
||||
@@ -76,7 +91,7 @@ this.DevToolsShim = {
|
||||
* - registerTheme
|
||||
* - unregisterTheme
|
||||
*
|
||||
* If DevTools are not installed when calling the method, DevToolsShim will call the
|
||||
* If DevTools are not initialized when calling the method, DevToolsShim will call the
|
||||
* appropriate method as soon as a gDevTools instance is registered.
|
||||
*/
|
||||
|
||||
@@ -86,7 +101,7 @@ this.DevToolsShim = {
|
||||
* - toolbox-destroyed
|
||||
*/
|
||||
on: function (event, listener) {
|
||||
if (this.isInstalled()) {
|
||||
if (this.isInitialized()) {
|
||||
this.gDevTools.on(event, listener);
|
||||
} else {
|
||||
this.listeners.push([event, listener]);
|
||||
@@ -98,7 +113,7 @@ this.DevToolsShim = {
|
||||
* with on().
|
||||
*/
|
||||
off: function (event, listener) {
|
||||
if (this.isInstalled()) {
|
||||
if (this.isInitialized()) {
|
||||
this.gDevTools.off(event, listener);
|
||||
} else {
|
||||
removeItem(this.listeners, ([e, l]) => e === event && l === listener);
|
||||
@@ -110,7 +125,7 @@ this.DevToolsShim = {
|
||||
* no longer supported.
|
||||
*/
|
||||
registerTool: function (tool) {
|
||||
if (this.isInstalled()) {
|
||||
if (this.isInitialized()) {
|
||||
this.gDevTools.registerTool(tool);
|
||||
} else {
|
||||
this.tools.push(tool);
|
||||
@@ -122,7 +137,7 @@ this.DevToolsShim = {
|
||||
* no longer supported.
|
||||
*/
|
||||
unregisterTool: function (tool) {
|
||||
if (this.isInstalled()) {
|
||||
if (this.isInitialized()) {
|
||||
this.gDevTools.unregisterTool(tool);
|
||||
} else {
|
||||
removeItem(this.tools, t => t === tool);
|
||||
@@ -134,7 +149,7 @@ this.DevToolsShim = {
|
||||
* no longer supported.
|
||||
*/
|
||||
registerTheme: function (theme) {
|
||||
if (this.isInstalled()) {
|
||||
if (this.isInitialized()) {
|
||||
this.gDevTools.registerTheme(theme);
|
||||
} else {
|
||||
this.themes.push(theme);
|
||||
@@ -146,7 +161,7 @@ this.DevToolsShim = {
|
||||
* no longer supported.
|
||||
*/
|
||||
unregisterTheme: function (theme) {
|
||||
if (this.isInstalled()) {
|
||||
if (this.isInitialized()) {
|
||||
this.gDevTools.unregisterTheme(theme);
|
||||
} else {
|
||||
removeItem(this.themes, t => t === theme);
|
||||
@@ -163,6 +178,11 @@ this.DevToolsShim = {
|
||||
if (!this.isInstalled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!this.isInitialized()) {
|
||||
this._initDevTools();
|
||||
}
|
||||
|
||||
return this.gDevTools.getOpenedScratchpads();
|
||||
},
|
||||
|
||||
@@ -174,6 +194,11 @@ this.DevToolsShim = {
|
||||
if (!this.isInstalled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isInitialized()) {
|
||||
this._initDevTools();
|
||||
}
|
||||
|
||||
this.gDevTools.restoreScratchpadSession(scratchpads);
|
||||
},
|
||||
|
||||
@@ -194,9 +219,19 @@ this.DevToolsShim = {
|
||||
if (!this.isInstalled()) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!this.isInitialized()) {
|
||||
this._initDevTools();
|
||||
}
|
||||
|
||||
return this.gDevTools.inspectNode(tab, selectors);
|
||||
},
|
||||
|
||||
_initDevTools: function () {
|
||||
let { loader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
loader.require("devtools/client/framework/devtools-browser");
|
||||
},
|
||||
|
||||
_onDevToolsRegistered: function () {
|
||||
// Register all pending event listeners on the real gDevTools object.
|
||||
for (let [event, listener] of this.listeners) {
|
||||
@@ -250,6 +285,10 @@ for (let method of [...addonSdkMethods, ...webExtensionsMethods]) {
|
||||
throw new Error(`Method ${method} unavailable if DevTools are not installed`);
|
||||
}
|
||||
|
||||
if (!this.isInitialized()) {
|
||||
this._initDevTools();
|
||||
}
|
||||
|
||||
return this.gDevTools[method].apply(this.gDevTools, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { DevToolsShim } =
|
||||
const { DevToolsShim: realDevToolsShim } =
|
||||
Components.utils.import("chrome://devtools-shim/content/DevToolsShim.jsm", {});
|
||||
|
||||
// Create a copy of the DevToolsShim for the test.
|
||||
const DevToolsShim = Object.assign({}, realDevToolsShim);
|
||||
|
||||
// Test the DevToolsShim
|
||||
|
||||
/**
|
||||
@@ -41,6 +44,14 @@ function createMockDevTools() {
|
||||
return mock;
|
||||
}
|
||||
|
||||
function mockDevToolsInstalled() {
|
||||
DevToolsShim.isInstalled = () => true;
|
||||
}
|
||||
|
||||
function mockDevToolsUninstalled() {
|
||||
DevToolsShim.isInstalled = () => false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given method was called an expected number of times, and finally check the
|
||||
* arguments provided to the last call, if appropriate.
|
||||
@@ -62,17 +73,17 @@ function checkCalls(mock, method, length, lastArgs) {
|
||||
}
|
||||
|
||||
function test_register_unregister() {
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
|
||||
DevToolsShim.register(createMockDevTools());
|
||||
ok(DevToolsShim.isInstalled(), "DevTools are installed");
|
||||
ok(DevToolsShim.isInitialized(), "DevTools are installed");
|
||||
|
||||
DevToolsShim.unregister();
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
}
|
||||
|
||||
function test_on_is_forwarded_to_devtools() {
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
|
||||
function cb1() {}
|
||||
function cb2() {}
|
||||
@@ -87,7 +98,7 @@ function test_on_is_forwarded_to_devtools() {
|
||||
}
|
||||
|
||||
function test_off_called_before_registering_devtools() {
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
|
||||
function cb1() {}
|
||||
let mock = createMockDevTools();
|
||||
@@ -100,7 +111,7 @@ function test_off_called_before_registering_devtools() {
|
||||
}
|
||||
|
||||
function test_off_called_before_with_bad_callback() {
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
|
||||
function cb1() {}
|
||||
function cb2() {}
|
||||
@@ -117,7 +128,7 @@ function test_off_called_before_with_bad_callback() {
|
||||
}
|
||||
|
||||
function test_registering_tool() {
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
|
||||
let tool1 = {};
|
||||
let tool2 = {};
|
||||
@@ -146,7 +157,7 @@ function test_registering_tool() {
|
||||
}
|
||||
|
||||
function test_registering_theme() {
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
|
||||
let theme1 = {};
|
||||
let theme2 = {};
|
||||
@@ -175,7 +186,7 @@ function test_registering_theme() {
|
||||
}
|
||||
|
||||
function test_events() {
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
|
||||
|
||||
let mock = createMockDevTools();
|
||||
// Check emit was not called.
|
||||
@@ -191,6 +202,8 @@ function test_events() {
|
||||
}
|
||||
|
||||
function test_scratchpad_apis() {
|
||||
mockDevToolsUninstalled();
|
||||
|
||||
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
|
||||
|
||||
// Check that restoreScratchpadSession doesn't crash.
|
||||
@@ -201,10 +214,13 @@ function test_scratchpad_apis() {
|
||||
"getOpenedScratchpads returns [] when DevTools are not installed");
|
||||
|
||||
let mock = createMockDevTools();
|
||||
DevToolsShim.register(mock);
|
||||
|
||||
// Check that calls to restoreScratchpadSession are not held.
|
||||
checkCalls(mock, "restoreScratchpadSession", 0);
|
||||
mockDevToolsInstalled();
|
||||
DevToolsShim._initDevTools = () => {
|
||||
// Next call to getOpenedScratchpags is expected to initialize DevTools, which we
|
||||
// simulate here by registering our mock.
|
||||
DevToolsShim.register(mock);
|
||||
};
|
||||
|
||||
DevToolsShim.getOpenedScratchpads();
|
||||
checkCalls(mock, "getOpenedScratchpads", 1, []);
|
||||
|
||||
@@ -25,6 +25,8 @@ enum class CompositeOperation : uint8_t;
|
||||
*/
|
||||
struct PropertyValuePair
|
||||
{
|
||||
explicit PropertyValuePair(nsCSSPropertyID aProperty)
|
||||
: mProperty(aProperty) { }
|
||||
PropertyValuePair(nsCSSPropertyID aProperty, nsCSSValue&& aValue)
|
||||
: mProperty(aProperty), mValue(Move(aValue)) { }
|
||||
PropertyValuePair(nsCSSPropertyID aProperty,
|
||||
|
||||
@@ -1402,13 +1402,14 @@ static inline bool
|
||||
RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
|
||||
{
|
||||
return aRange->GetStartContainer() == aNode &&
|
||||
aRange->StartOffset() == aOffset;
|
||||
static_cast<int32_t>(aRange->StartOffset()) == aOffset;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
|
||||
{
|
||||
return aRange->GetEndContainer() == aNode && aRange->EndOffset() == aOffset;
|
||||
return aRange->GetEndContainer() == aNode &&
|
||||
static_cast<int32_t>(aRange->EndOffset()) == aOffset;
|
||||
}
|
||||
|
||||
// Selection::EqualsRangeAtPoint
|
||||
|
||||
@@ -2774,6 +2774,9 @@ nsContentUtils::ComparePoints(nsINode* aParent1, int32_t aOffset1,
|
||||
bool* aDisconnected)
|
||||
{
|
||||
if (aParent1 == aParent2) {
|
||||
// XXX This is odd. aOffset1 and/or aOffset2 may be -1, e.g., it's result
|
||||
// of nsINode::IndexOf(), but this compares such invalid offset with
|
||||
// valid offset.
|
||||
return aOffset1 < aOffset2 ? -1 :
|
||||
aOffset1 > aOffset2 ? 1 :
|
||||
0;
|
||||
@@ -2824,10 +2827,14 @@ nsContentUtils::ComparePoints(nsINode* aParent1, int32_t aOffset1,
|
||||
|
||||
if (!pos1) {
|
||||
nsINode* child2 = parents2.ElementAt(--pos2);
|
||||
// XXX aOffset1 may be -1 as mentioned above. So, why does this return
|
||||
// it's *before* of the valid DOM point?
|
||||
return aOffset1 <= parent->IndexOf(child2) ? -1 : 1;
|
||||
}
|
||||
|
||||
nsINode* child1 = parents1.ElementAt(--pos1);
|
||||
// XXX aOffset2 may be -1 as mentioned above. So, why does this return it's
|
||||
// *after* of the valid DOM point?
|
||||
return parent->IndexOf(child1) < aOffset2 ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -404,6 +404,13 @@ public:
|
||||
* NOTE! If the two nodes aren't in the same connected subtree,
|
||||
* the result is 1, and the optional aDisconnected parameter
|
||||
* is set to true.
|
||||
*
|
||||
* XXX aOffset1 and aOffset2 should be uint32_t since valid offset value is
|
||||
* between 0 - UINT32_MAX. However, these methods work even with
|
||||
* negative offset values! E.g., when aOffset1 is -1 and aOffset is 0,
|
||||
* these methods return -1. Some root callers depend on this behavior.
|
||||
* On the other hand, nsINode can have ATTRCHILD_ARRAY_MAX_CHILD_COUN
|
||||
* (0x3FFFFF) at most. Therefore, they can be int32_t for now.
|
||||
*/
|
||||
static int32_t ComparePoints(nsINode* aParent1, int32_t aOffset1,
|
||||
nsINode* aParent2, int32_t aOffset2,
|
||||
|
||||
@@ -1569,10 +1569,13 @@ nsHTMLCopyEncoder::IncludeInContext(nsINode *aNode)
|
||||
nsresult
|
||||
nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
|
||||
{
|
||||
if (!inRange) return NS_ERROR_NULL_POINTER;
|
||||
RefPtr<nsRange> range = static_cast<nsRange*>(inRange);
|
||||
if (!range) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode, common;
|
||||
int32_t startOffset, endOffset;
|
||||
uint32_t startOffset, endOffset;
|
||||
|
||||
rv = inRange->GetCommonAncestorContainer(getter_AddRefs(common));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@@ -1590,9 +1593,11 @@ nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
|
||||
int32_t opStartOffset, opEndOffset;
|
||||
|
||||
// examine range endpoints.
|
||||
rv = GetPromotedPoint( kStart, startNode, startOffset, address_of(opStartNode), &opStartOffset, common);
|
||||
rv = GetPromotedPoint(kStart, startNode, static_cast<int32_t>(startOffset),
|
||||
address_of(opStartNode), &opStartOffset, common);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = GetPromotedPoint( kEnd, endNode, endOffset, address_of(opEndNode), &opEndOffset, common);
|
||||
rv = GetPromotedPoint(kEnd, endNode, static_cast<int32_t>(endOffset),
|
||||
address_of(opEndNode), &opEndOffset, common);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// if both range endpoints are at the common ancestor, check for possible inclusion of ancestors
|
||||
@@ -1604,9 +1609,9 @@ nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
|
||||
}
|
||||
|
||||
// set the range to the new values
|
||||
rv = inRange->SetStart(opStartNode, opStartOffset);
|
||||
rv = inRange->SetStart(opStartNode, static_cast<uint32_t>(opStartOffset));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inRange->SetEnd(opEndNode, opEndOffset);
|
||||
rv = inRange->SetEnd(opEndNode, static_cast<uint32_t>(opEndOffset));
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -2512,7 +2512,7 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
||||
bool isCollapsed = false;
|
||||
nsCOMPtr<nsIContent> startContent, endContent;
|
||||
int32_t startOffset = 0;
|
||||
uint32_t startOffset = 0;
|
||||
if (domSelection) {
|
||||
domSelection->GetIsCollapsed(&isCollapsed);
|
||||
nsCOMPtr<nsIDOMRange> domRange;
|
||||
@@ -2526,7 +2526,6 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
|
||||
|
||||
startContent = do_QueryInterface(startNode);
|
||||
if (startContent && startContent->IsElement()) {
|
||||
NS_ASSERTION(startOffset >= 0, "Start offset cannot be negative");
|
||||
childContent = startContent->GetChildAt(startOffset);
|
||||
if (childContent) {
|
||||
startContent = childContent;
|
||||
@@ -2535,9 +2534,8 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
|
||||
|
||||
endContent = do_QueryInterface(endNode);
|
||||
if (endContent && endContent->IsElement()) {
|
||||
int32_t endOffset = 0;
|
||||
uint32_t endOffset = 0;
|
||||
domRange->GetEndOffset(&endOffset);
|
||||
NS_ASSERTION(endOffset >= 0, "End offset cannot be negative");
|
||||
childContent = endContent->GetChildAt(endOffset);
|
||||
if (childContent) {
|
||||
endContent = childContent;
|
||||
@@ -2565,7 +2563,7 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
|
||||
bool isFormControl =
|
||||
startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
|
||||
|
||||
if (nodeValue.Length() == (uint32_t)startOffset && !isFormControl &&
|
||||
if (nodeValue.Length() == startOffset && !isFormControl &&
|
||||
startContent != aDocument->GetRootElement()) {
|
||||
// Yes, indeed we were at the end of the last node
|
||||
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
||||
|
||||
@@ -2738,6 +2738,11 @@ nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
|
||||
if (IsRemoteFrame()) {
|
||||
if (mRemoteBrowser) {
|
||||
ScreenIntSize size = aIFrame->GetSubdocumentSize();
|
||||
// If we were not able to show remote frame before, we should probably
|
||||
// retry now to send correct showInfo.
|
||||
if (!mRemoteBrowserShown) {
|
||||
ShowRemoteFrame(size, aIFrame);
|
||||
}
|
||||
nsIntRect dimensions;
|
||||
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
|
||||
mLazySize = size;
|
||||
|
||||
@@ -111,31 +111,37 @@ nsRange::CompareNodeToRange(nsINode* aNode, nsRange* aRange,
|
||||
// so instead represent it by (node,0) and (node,numChildren)
|
||||
parent = aNode;
|
||||
nodeStart = 0;
|
||||
nodeEnd = aNode->GetChildCount();
|
||||
uint32_t childCount = aNode->GetChildCount();
|
||||
MOZ_ASSERT(childCount <= INT32_MAX,
|
||||
"There shouldn't be over INT32_MAX children");
|
||||
nodeEnd = static_cast<int32_t>(childCount);
|
||||
}
|
||||
else {
|
||||
nodeStart = parent->IndexOf(aNode);
|
||||
nodeEnd = nodeStart + 1;
|
||||
MOZ_ASSERT(nodeStart < nodeEnd, "nodeStart shouldn't be INT32_MAX");
|
||||
}
|
||||
|
||||
nsINode* rangeStartContainer = aRange->GetStartContainer();
|
||||
nsINode* rangeEndContainer = aRange->GetEndContainer();
|
||||
int32_t rangeStartOffset = aRange->StartOffset();
|
||||
int32_t rangeEndOffset = aRange->EndOffset();
|
||||
uint32_t rangeStartOffset = aRange->StartOffset();
|
||||
uint32_t rangeEndOffset = aRange->EndOffset();
|
||||
|
||||
// is RANGE(start) <= NODE(start) ?
|
||||
bool disconnected = false;
|
||||
*outNodeBefore = nsContentUtils::ComparePoints(rangeStartContainer,
|
||||
rangeStartOffset,
|
||||
parent, nodeStart,
|
||||
&disconnected) > 0;
|
||||
*outNodeBefore =
|
||||
nsContentUtils::ComparePoints(rangeStartContainer,
|
||||
static_cast<int32_t>(rangeStartOffset),
|
||||
parent, nodeStart,
|
||||
&disconnected) > 0;
|
||||
NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
|
||||
|
||||
// is RANGE(end) >= NODE(end) ?
|
||||
*outNodeAfter = nsContentUtils::ComparePoints(rangeEndContainer,
|
||||
rangeEndOffset,
|
||||
parent, nodeEnd,
|
||||
&disconnected) < 0;
|
||||
*outNodeAfter =
|
||||
nsContentUtils::ComparePoints(rangeEndContainer,
|
||||
static_cast<int32_t>(rangeEndOffset),
|
||||
parent, nodeEnd,
|
||||
&disconnected) < 0;
|
||||
NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -164,13 +170,17 @@ struct IsItemInRangeComparator
|
||||
|
||||
int operator()(const nsRange* const aRange) const
|
||||
{
|
||||
int32_t cmp = nsContentUtils::ComparePoints(mNode, mEndOffset,
|
||||
aRange->GetStartContainer(),
|
||||
aRange->StartOffset());
|
||||
int32_t cmp =
|
||||
nsContentUtils::ComparePoints(
|
||||
mNode, static_cast<int32_t>(mEndOffset),
|
||||
aRange->GetStartContainer(),
|
||||
static_cast<int32_t>(aRange->StartOffset()));
|
||||
if (cmp == 1) {
|
||||
cmp = nsContentUtils::ComparePoints(mNode, mStartOffset,
|
||||
aRange->GetEndContainer(),
|
||||
aRange->EndOffset());
|
||||
cmp =
|
||||
nsContentUtils::ComparePoints(
|
||||
mNode, static_cast<int32_t>(mStartOffset),
|
||||
aRange->GetEndContainer(),
|
||||
static_cast<int32_t>(aRange->EndOffset()));
|
||||
if (cmp == -1) {
|
||||
return 0;
|
||||
}
|
||||
@@ -266,8 +276,8 @@ nsRange::nsRange(nsINode* aNode)
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsRange::CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
|
||||
nsINode* aEndParent, int32_t aEndOffset,
|
||||
nsRange::CreateRange(nsINode* aStartContainer, uint32_t aStartOffset,
|
||||
nsINode* aEndParent, uint32_t aEndOffset,
|
||||
nsRange** aRange)
|
||||
{
|
||||
MOZ_ASSERT(aRange);
|
||||
@@ -285,8 +295,8 @@ nsRange::CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsRange::CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
|
||||
nsIDOMNode* aEndParent, int32_t aEndOffset,
|
||||
nsRange::CreateRange(nsIDOMNode* aStartContainer, uint32_t aStartOffset,
|
||||
nsIDOMNode* aEndParent, uint32_t aEndOffset,
|
||||
nsRange** aRange)
|
||||
{
|
||||
nsCOMPtr<nsINode> startContainer = do_QueryInterface(aStartContainer);
|
||||
@@ -297,8 +307,8 @@ nsRange::CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsRange::CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
|
||||
nsIDOMNode* aEndParent, int32_t aEndOffset,
|
||||
nsRange::CreateRange(nsIDOMNode* aStartContainer, uint32_t aStartOffset,
|
||||
nsIDOMNode* aEndParent, uint32_t aEndOffset,
|
||||
nsIDOMRange** aRange)
|
||||
{
|
||||
RefPtr<nsRange> range;
|
||||
@@ -456,17 +466,27 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
|
||||
// again (when the new text node is notified).
|
||||
nsINode* parentNode = aContent->GetParentNode();
|
||||
int32_t index = -1;
|
||||
if (parentNode == mEndContainer && mEndOffset > 0 &&
|
||||
(index = parentNode->IndexOf(aContent)) + 1 == mEndOffset) {
|
||||
newEndNode = mEndContainer;
|
||||
newEndOffset = mEndOffset + 1;
|
||||
mEndOffsetWasIncremented = true;
|
||||
if (parentNode == mEndContainer && mEndOffset > 0) {
|
||||
index = parentNode->IndexOf(aContent);
|
||||
NS_WARNING_ASSERTION(index >= 0,
|
||||
"Shouldn't be called during removing the node or something");
|
||||
if (static_cast<uint32_t>(index + 1) == mEndOffset) {
|
||||
newEndNode = mEndContainer;
|
||||
newEndOffset = mEndOffset + 1;
|
||||
MOZ_ASSERT(IsValidOffset(newEndOffset));
|
||||
mEndOffsetWasIncremented = true;
|
||||
}
|
||||
}
|
||||
if (parentNode == mStartContainer && mStartOffset > 0 &&
|
||||
(index != -1 ? index : parentNode->IndexOf(aContent)) + 1 == mStartOffset) {
|
||||
newStartNode = mStartContainer;
|
||||
newStartOffset = mStartOffset + 1;
|
||||
mStartOffsetWasIncremented = true;
|
||||
if (parentNode == mStartContainer && mStartOffset > 0) {
|
||||
if (index <= 0) {
|
||||
index = parentNode->IndexOf(aContent);
|
||||
}
|
||||
if (static_cast<uint32_t>(index + 1) == mStartOffset) {
|
||||
newStartNode = mStartContainer;
|
||||
newStartOffset = mStartOffset + 1;
|
||||
MOZ_ASSERT(IsValidOffset(newStartOffset));
|
||||
mStartOffsetWasIncremented = true;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
|
||||
@@ -479,16 +499,15 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
|
||||
|
||||
// If the changed node contains our start boundary and the change starts
|
||||
// before the boundary we'll need to adjust the offset.
|
||||
if (aContent == mStartContainer &&
|
||||
aInfo->mChangeStart < static_cast<uint32_t>(mStartOffset)) {
|
||||
if (aContent == mStartContainer && aInfo->mChangeStart < mStartOffset) {
|
||||
if (aInfo->mDetails) {
|
||||
// splitText(), aInfo->mDetails->mNextSibling is the new text node
|
||||
NS_ASSERTION(aInfo->mDetails->mType ==
|
||||
CharacterDataChangeInfo::Details::eSplit,
|
||||
"only a split can start before the end");
|
||||
NS_ASSERTION(static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd + 1,
|
||||
NS_ASSERTION(mStartOffset <= aInfo->mChangeEnd + 1,
|
||||
"mStartOffset is beyond the end of this node");
|
||||
newStartOffset = static_cast<uint32_t>(mStartOffset) - aInfo->mChangeStart;
|
||||
newStartOffset = mStartOffset - aInfo->mChangeStart;
|
||||
newStartNode = aInfo->mDetails->mNextSibling;
|
||||
if (MOZ_UNLIKELY(aContent == mRoot)) {
|
||||
newRoot = IsValidBoundary(newStartNode);
|
||||
@@ -507,7 +526,7 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
|
||||
// If boundary is inside changed text, position it before change
|
||||
// else adjust start offset for the change in length.
|
||||
newStartNode = mStartContainer;
|
||||
newStartOffset = static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd ?
|
||||
newStartOffset = mStartOffset <= aInfo->mChangeEnd ?
|
||||
aInfo->mChangeStart :
|
||||
mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
|
||||
aInfo->mReplaceLength;
|
||||
@@ -517,16 +536,15 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
|
||||
// Do the same thing for the end boundary, except for splitText of a node
|
||||
// with no parent then only switch to the new node if the start boundary
|
||||
// did so too (otherwise the range would end up with disconnected nodes).
|
||||
if (aContent == mEndContainer &&
|
||||
aInfo->mChangeStart < static_cast<uint32_t>(mEndOffset)) {
|
||||
if (aContent == mEndContainer && aInfo->mChangeStart < mEndOffset) {
|
||||
if (aInfo->mDetails && (aContent->GetParentNode() || newStartNode)) {
|
||||
// splitText(), aInfo->mDetails->mNextSibling is the new text node
|
||||
NS_ASSERTION(aInfo->mDetails->mType ==
|
||||
CharacterDataChangeInfo::Details::eSplit,
|
||||
"only a split can start before the end");
|
||||
NS_ASSERTION(static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd + 1,
|
||||
NS_ASSERTION(mEndOffset <= aInfo->mChangeEnd + 1,
|
||||
"mEndOffset is beyond the end of this node");
|
||||
newEndOffset = static_cast<uint32_t>(mEndOffset) - aInfo->mChangeStart;
|
||||
newEndOffset = mEndOffset - aInfo->mChangeStart;
|
||||
newEndNode = aInfo->mDetails->mNextSibling;
|
||||
|
||||
bool isCommonAncestor =
|
||||
@@ -542,7 +560,7 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
|
||||
}
|
||||
} else {
|
||||
newEndNode = mEndContainer;
|
||||
newEndOffset = static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd ?
|
||||
newEndOffset = mEndOffset <= aInfo->mChangeEnd ?
|
||||
aInfo->mChangeStart :
|
||||
mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
|
||||
aInfo->mReplaceLength;
|
||||
@@ -555,14 +573,14 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
|
||||
// that will be removed
|
||||
nsIContent* removed = aInfo->mDetails->mNextSibling;
|
||||
if (removed == mStartContainer) {
|
||||
newStartOffset = static_cast<uint32_t>(mStartOffset) + aInfo->mChangeStart;
|
||||
newStartOffset = mStartOffset + aInfo->mChangeStart;
|
||||
newStartNode = aContent;
|
||||
if (MOZ_UNLIKELY(removed == mRoot)) {
|
||||
newRoot = IsValidBoundary(newStartNode);
|
||||
}
|
||||
}
|
||||
if (removed == mEndContainer) {
|
||||
newEndOffset = static_cast<uint32_t>(mEndOffset) + aInfo->mChangeStart;
|
||||
newEndOffset = mEndOffset + aInfo->mChangeStart;
|
||||
newEndNode = aContent;
|
||||
if (MOZ_UNLIKELY(removed == mRoot)) {
|
||||
newRoot = IsValidBoundary(newEndNode);
|
||||
@@ -576,13 +594,13 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
|
||||
// point before the first child is never affected by normalize().)
|
||||
nsINode* parentNode = aContent->GetParentNode();
|
||||
if (parentNode == mStartContainer && mStartOffset > 0 &&
|
||||
uint32_t(mStartOffset) < parentNode->GetChildCount() &&
|
||||
mStartOffset < parentNode->GetChildCount() &&
|
||||
removed == parentNode->GetChildAt(mStartOffset)) {
|
||||
newStartNode = aContent;
|
||||
newStartOffset = aInfo->mChangeStart;
|
||||
}
|
||||
if (parentNode == mEndContainer && mEndOffset > 0 &&
|
||||
uint32_t(mEndOffset) < parentNode->GetChildCount() &&
|
||||
mEndOffset < parentNode->GetChildCount() &&
|
||||
removed == parentNode->GetChildAt(mEndOffset)) {
|
||||
newEndNode = aContent;
|
||||
newEndOffset = aInfo->mChangeEnd;
|
||||
@@ -650,14 +668,20 @@ nsRange::ContentInserted(nsIDocument* aDocument,
|
||||
nsINode* container = NODE_FROM(aContainer, aDocument);
|
||||
|
||||
// Adjust position if a sibling was inserted.
|
||||
if (container == mStartContainer && aIndexInContainer < mStartOffset &&
|
||||
if (container == mStartContainer &&
|
||||
(NS_WARN_IF(aIndexInContainer < 0) ||
|
||||
static_cast<uint32_t>(aIndexInContainer) < mStartOffset) &&
|
||||
!mStartOffsetWasIncremented) {
|
||||
++newStartOffset;
|
||||
MOZ_ASSERT(IsValidOffset(newStartOffset));
|
||||
rangeChanged = true;
|
||||
}
|
||||
if (container == mEndContainer && aIndexInContainer < mEndOffset &&
|
||||
if (container == mEndContainer &&
|
||||
(NS_WARN_IF(aIndexInContainer < 0) ||
|
||||
static_cast<uint32_t>(aIndexInContainer) < mEndOffset) &&
|
||||
!mEndOffsetWasIncremented) {
|
||||
++newEndOffset;
|
||||
MOZ_ASSERT(IsValidOffset(newEndOffset));
|
||||
rangeChanged = true;
|
||||
}
|
||||
if (container->IsSelectionDescendant() &&
|
||||
@@ -705,7 +729,7 @@ nsRange::ContentRemoved(nsIDocument* aDocument,
|
||||
|
||||
// Adjust position if a sibling was removed...
|
||||
if (container == mStartContainer) {
|
||||
if (aIndexInContainer < mStartOffset) {
|
||||
if (aIndexInContainer < static_cast<int32_t>(mStartOffset)) {
|
||||
--newStartOffset;
|
||||
rangeChanged = true;
|
||||
}
|
||||
@@ -717,7 +741,7 @@ nsRange::ContentRemoved(nsIDocument* aDocument,
|
||||
|
||||
// Do same thing for end boundry.
|
||||
if (container == mEndContainer) {
|
||||
if (aIndexInContainer < mEndOffset) {
|
||||
if (aIndexInContainer < static_cast<int32_t>(mEndOffset)) {
|
||||
--newEndOffset;
|
||||
rangeChanged = true;
|
||||
}
|
||||
@@ -773,12 +797,15 @@ nsRange::ParentChainChanged(nsIContent *aContent)
|
||||
* Utilities for comparing points: API from nsIDOMRange
|
||||
******************************************************/
|
||||
NS_IMETHODIMP
|
||||
nsRange::IsPointInRange(nsIDOMNode* aContainer, int32_t aOffset, bool* aResult)
|
||||
nsRange::IsPointInRange(nsIDOMNode* aContainer, uint32_t aOffset, bool* aResult)
|
||||
{
|
||||
nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
|
||||
if (!container) {
|
||||
return NS_ERROR_DOM_NOT_OBJECT_ERR;
|
||||
}
|
||||
if (NS_WARN_IF(!IsValidOffset(aOffset))) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
*aResult = IsPointInRange(*container, aOffset, rv);
|
||||
@@ -801,7 +828,8 @@ nsRange::IsPointInRange(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
|
||||
// returns -1 if point is before range, 0 if point is in range,
|
||||
// 1 if point is after range.
|
||||
NS_IMETHODIMP
|
||||
nsRange::ComparePoint(nsIDOMNode* aContainer, int32_t aOffset, int16_t* aResult)
|
||||
nsRange::ComparePoint(nsIDOMNode* aContainer, uint32_t aOffset,
|
||||
int16_t* aResult)
|
||||
{
|
||||
nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
|
||||
NS_ENSURE_TRUE(container, NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
||||
@@ -835,15 +863,18 @@ nsRange::ComparePoint(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t cmp;
|
||||
if ((cmp = nsContentUtils::ComparePoints(&aContainer, aOffset,
|
||||
mStartContainer,
|
||||
mStartOffset)) <= 0) {
|
||||
|
||||
int32_t cmp =
|
||||
nsContentUtils::ComparePoints(&aContainer,
|
||||
static_cast<int32_t>(aOffset),
|
||||
mStartContainer,
|
||||
static_cast<int32_t>(mStartOffset));
|
||||
if (cmp <= 0) {
|
||||
return cmp;
|
||||
}
|
||||
if (nsContentUtils::ComparePoints(mEndContainer, mEndOffset,
|
||||
&aContainer, aOffset) == -1) {
|
||||
if (nsContentUtils::ComparePoints(mEndContainer,
|
||||
static_cast<int32_t>(mEndOffset),
|
||||
&aContainer,
|
||||
static_cast<int32_t>(aOffset)) == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -886,12 +917,15 @@ nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
|
||||
// Steps 6-7.
|
||||
// Note: if disconnected is true, ComparePoints returns 1.
|
||||
bool disconnected = false;
|
||||
bool result = nsContentUtils::ComparePoints(mStartContainer, mStartOffset,
|
||||
parent, nodeIndex + 1,
|
||||
&disconnected) < 0 &&
|
||||
nsContentUtils::ComparePoints(parent, nodeIndex,
|
||||
mEndContainer, mEndOffset,
|
||||
&disconnected) < 0;
|
||||
bool result =
|
||||
nsContentUtils::ComparePoints(mStartContainer,
|
||||
static_cast<int32_t>(mStartOffset),
|
||||
parent, nodeIndex + 1,
|
||||
&disconnected) < 0 &&
|
||||
nsContentUtils::ComparePoints(parent, nodeIndex,
|
||||
mEndContainer,
|
||||
static_cast<int32_t>(mEndOffset),
|
||||
&disconnected) < 0;
|
||||
|
||||
// Step 2.
|
||||
if (disconnected) {
|
||||
@@ -910,8 +944,8 @@ nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
|
||||
// Calling DoSetRange with either parent argument null will collapse
|
||||
// the range to have both endpoints point to the other node
|
||||
void
|
||||
nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset,
|
||||
nsINode* aEndN, int32_t aEndOffset,
|
||||
nsRange::DoSetRange(nsINode* aStartN, uint32_t aStartOffset,
|
||||
nsINode* aEndN, uint32_t aEndOffset,
|
||||
nsINode* aRoot, bool aNotInsertedYet)
|
||||
{
|
||||
NS_PRECONDITION((aStartN && aEndN && aRoot) ||
|
||||
@@ -937,6 +971,8 @@ nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset,
|
||||
/*For backward compatibility*/
|
||||
aRoot->IsNodeOfType(nsINode::eCONTENT))),
|
||||
"Bad root");
|
||||
MOZ_ASSERT(IsValidOffset(aStartOffset));
|
||||
MOZ_ASSERT(IsValidOffset(aEndOffset));
|
||||
|
||||
if (mRoot != aRoot) {
|
||||
if (mRoot) {
|
||||
@@ -1059,7 +1095,7 @@ nsRange::GetStartContainer(ErrorResult& aRv) const
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsRange::GetStartOffset(int32_t* aStartOffset)
|
||||
nsRange::GetStartOffset(uint32_t* aStartOffset)
|
||||
{
|
||||
if (!mIsPositioned)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
@@ -1101,7 +1137,7 @@ nsRange::GetEndContainer(ErrorResult& aRv) const
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsRange::GetEndOffset(int32_t* aEndOffset)
|
||||
nsRange::GetEndOffset(uint32_t* aEndOffset)
|
||||
{
|
||||
if (!mIsPositioned)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
@@ -1160,10 +1196,10 @@ nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent)
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsRange::IsValidOffset(nsINode* aNode, int32_t aOffset)
|
||||
nsRange::IsValidOffset(nsINode* aNode, uint32_t aOffset)
|
||||
{
|
||||
return aNode &&
|
||||
aOffset >= 0 &&
|
||||
IsValidOffset(aOffset) &&
|
||||
static_cast<size_t>(aOffset) <= aNode->Length();
|
||||
}
|
||||
|
||||
@@ -1235,7 +1271,7 @@ nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsRange::SetStart(nsIDOMNode* aContainer, int32_t aOffset)
|
||||
nsRange::SetStart(nsIDOMNode* aContainer, uint32_t aOffset)
|
||||
{
|
||||
nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
|
||||
if (!container) {
|
||||
@@ -1248,7 +1284,7 @@ nsRange::SetStart(nsIDOMNode* aContainer, int32_t aOffset)
|
||||
}
|
||||
|
||||
/* virtual */ nsresult
|
||||
nsRange::SetStart(nsINode* aContainer, int32_t aOffset)
|
||||
nsRange::SetStart(nsINode* aContainer, uint32_t aOffset)
|
||||
{
|
||||
nsINode* newRoot = IsValidBoundary(aContainer);
|
||||
if (!newRoot) {
|
||||
@@ -1262,8 +1298,10 @@ nsRange::SetStart(nsINode* aContainer, int32_t aOffset)
|
||||
// Collapse if not positioned yet, if positioned in another doc or
|
||||
// if the new start is after end.
|
||||
if (!mIsPositioned || newRoot != mRoot ||
|
||||
nsContentUtils::ComparePoints(aContainer, aOffset,
|
||||
mEndContainer, mEndOffset) == 1) {
|
||||
nsContentUtils::ComparePoints(aContainer,
|
||||
static_cast<int32_t>(aOffset),
|
||||
mEndContainer,
|
||||
static_cast<int32_t>(mEndOffset)) == 1) {
|
||||
DoSetRange(aContainer, aOffset, aContainer, aOffset, newRoot);
|
||||
|
||||
return NS_OK;
|
||||
@@ -1292,7 +1330,10 @@ nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
AutoInvalidateSelection atEndOfBlock(this);
|
||||
int32_t offset = -1;
|
||||
// If the node is being removed from its parent, GetContainerAndOffsetBefore()
|
||||
// returns nullptr. Then, SetStart() will throw
|
||||
// NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
|
||||
uint32_t offset = UINT32_MAX;
|
||||
nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
|
||||
aRv = SetStart(container, offset);
|
||||
}
|
||||
@@ -1328,7 +1369,10 @@ nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
AutoInvalidateSelection atEndOfBlock(this);
|
||||
int32_t offset = -1;
|
||||
// If the node is being removed from its parent, GetContainerAndOffsetAfter()
|
||||
// returns nullptr. Then, SetStart() will throw
|
||||
// NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
|
||||
uint32_t offset = UINT32_MAX;
|
||||
nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
|
||||
aRv = SetStart(container, offset);
|
||||
}
|
||||
@@ -1367,7 +1411,7 @@ nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsRange::SetEnd(nsIDOMNode* aContainer, int32_t aOffset)
|
||||
nsRange::SetEnd(nsIDOMNode* aContainer, uint32_t aOffset)
|
||||
{
|
||||
nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
|
||||
if (!container) {
|
||||
@@ -1380,7 +1424,7 @@ nsRange::SetEnd(nsIDOMNode* aContainer, int32_t aOffset)
|
||||
}
|
||||
|
||||
/* virtual */ nsresult
|
||||
nsRange::SetEnd(nsINode* aContainer, int32_t aOffset)
|
||||
nsRange::SetEnd(nsINode* aContainer, uint32_t aOffset)
|
||||
{
|
||||
nsINode* newRoot = IsValidBoundary(aContainer);
|
||||
if (!newRoot) {
|
||||
@@ -1394,8 +1438,10 @@ nsRange::SetEnd(nsINode* aContainer, int32_t aOffset)
|
||||
// Collapse if not positioned yet, if positioned in another doc or
|
||||
// if the new end is before start.
|
||||
if (!mIsPositioned || newRoot != mRoot ||
|
||||
nsContentUtils::ComparePoints(mStartContainer, mStartOffset,
|
||||
aContainer, aOffset) == 1) {
|
||||
nsContentUtils::ComparePoints(mStartContainer,
|
||||
static_cast<int32_t>(mStartOffset),
|
||||
aContainer,
|
||||
static_cast<int32_t>(aOffset)) == 1) {
|
||||
DoSetRange(aContainer, aOffset, aContainer, aOffset, newRoot);
|
||||
|
||||
return NS_OK;
|
||||
@@ -1407,8 +1453,8 @@ nsRange::SetEnd(nsINode* aContainer, int32_t aOffset)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRange::SetStartAndEnd(nsINode* aStartContainer, int32_t aStartOffset,
|
||||
nsINode* aEndContainer, int32_t aEndOffset)
|
||||
nsRange::SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
|
||||
nsINode* aEndContainer, uint32_t aEndOffset)
|
||||
{
|
||||
if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
@@ -1455,8 +1501,10 @@ nsRange::SetStartAndEnd(nsINode* aStartContainer, int32_t aStartOffset,
|
||||
|
||||
// If the end point is before the start point, this should be collapsed at
|
||||
// the end point.
|
||||
if (nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
|
||||
aEndContainer, aEndOffset) == 1) {
|
||||
if (nsContentUtils::ComparePoints(aStartContainer,
|
||||
static_cast<int32_t>(aStartOffset),
|
||||
aEndContainer,
|
||||
static_cast<int32_t>(aEndOffset)) == 1) {
|
||||
DoSetRange(aEndContainer, aEndOffset,
|
||||
aEndContainer, aEndOffset, newEndRoot);
|
||||
return NS_OK;
|
||||
@@ -1486,7 +1534,10 @@ nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
AutoInvalidateSelection atEndOfBlock(this);
|
||||
int32_t offset = -1;
|
||||
// If the node is being removed from its parent, GetContainerAndOffsetBefore()
|
||||
// returns nullptr. Then, SetEnd() will throw
|
||||
// NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
|
||||
uint32_t offset = UINT32_MAX;
|
||||
nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
|
||||
aRv = SetEnd(container, offset);
|
||||
}
|
||||
@@ -1522,7 +1573,10 @@ nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
AutoInvalidateSelection atEndOfBlock(this);
|
||||
int32_t offset = -1;
|
||||
// If the node is being removed from its parent, GetContainerAndOffsetAfter()
|
||||
// returns nullptr. Then, SetEnd() will throw
|
||||
// NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
|
||||
uint32_t offset = UINT32_MAX;
|
||||
nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
|
||||
aRv = SetEnd(container, offset);
|
||||
}
|
||||
@@ -1602,7 +1656,9 @@ nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
int32_t index = container->IndexOf(&aNode);
|
||||
if (index < 0) {
|
||||
if (NS_WARN_IF(index < 0) ||
|
||||
!IsValidOffset(static_cast<uint32_t>(index)) ||
|
||||
!IsValidOffset(static_cast<uint32_t>(index) + 1)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
@@ -2059,9 +2115,9 @@ nsRange::CutContents(DocumentFragment** aFragment)
|
||||
// of Range gravity during our edits!
|
||||
|
||||
nsCOMPtr<nsINode> startContainer = mStartContainer;
|
||||
int32_t startOffset = mStartOffset;
|
||||
uint32_t startOffset = mStartOffset;
|
||||
nsCOMPtr<nsINode> endContainer = mEndContainer;
|
||||
int32_t endOffset = mEndOffset;
|
||||
uint32_t endOffset = mEndOffset;
|
||||
|
||||
if (retval) {
|
||||
// For extractContents(), abort early if there's a doctype (bug 719533).
|
||||
@@ -2072,10 +2128,12 @@ nsRange::CutContents(DocumentFragment** aFragment)
|
||||
RefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype();
|
||||
|
||||
if (doctype &&
|
||||
nsContentUtils::ComparePoints(startContainer, startOffset,
|
||||
nsContentUtils::ComparePoints(startContainer,
|
||||
static_cast<int32_t>(startOffset),
|
||||
doctype, 0) < 0 &&
|
||||
nsContentUtils::ComparePoints(doctype, 0,
|
||||
endContainer, endOffset) < 0) {
|
||||
endContainer,
|
||||
static_cast<int32_t>(endOffset)) < 0) {
|
||||
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
||||
}
|
||||
}
|
||||
@@ -2170,8 +2228,7 @@ nsRange::CutContents(DocumentFragment** aFragment)
|
||||
rv = charData->GetLength(&dataLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (dataLength >= (uint32_t)startOffset)
|
||||
{
|
||||
if (dataLength >= startOffset) {
|
||||
nsMutationGuard guard;
|
||||
nsCOMPtr<nsIDOMCharacterData> cutNode;
|
||||
rv = SplitDataNode(charData, startOffset, getter_AddRefs(cutNode));
|
||||
@@ -2187,22 +2244,17 @@ nsRange::CutContents(DocumentFragment** aFragment)
|
||||
else if (node == endContainer)
|
||||
{
|
||||
// Delete or extract everything before endOffset.
|
||||
|
||||
if (endOffset >= 0)
|
||||
{
|
||||
nsMutationGuard guard;
|
||||
nsCOMPtr<nsIDOMCharacterData> cutNode;
|
||||
/* The Range spec clearly states clones get cut and original nodes
|
||||
remain behind, so use false as the last parameter.
|
||||
*/
|
||||
rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode),
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_STATE(!guard.Mutated(1) ||
|
||||
ValidateCurrentNode(this, iter));
|
||||
nodeToResult = do_QueryInterface(cutNode);
|
||||
}
|
||||
|
||||
nsMutationGuard guard;
|
||||
nsCOMPtr<nsIDOMCharacterData> cutNode;
|
||||
/* The Range spec clearly states clones get cut and original nodes
|
||||
remain behind, so use false as the last parameter.
|
||||
*/
|
||||
rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode),
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_STATE(!guard.Mutated(1) ||
|
||||
ValidateCurrentNode(this, iter));
|
||||
nodeToResult = do_QueryInterface(cutNode);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
@@ -2212,8 +2264,7 @@ nsRange::CutContents(DocumentFragment** aFragment)
|
||||
if (node && node->IsElement() &&
|
||||
((node == endContainer && endOffset == 0) ||
|
||||
(node == startContainer &&
|
||||
int32_t(node->AsElement()->GetChildCount()) == startOffset)))
|
||||
{
|
||||
node->AsElement()->GetChildCount() == startOffset))) {
|
||||
if (retval) {
|
||||
ErrorResult rv;
|
||||
nodeToResult = node->CloneNode(false, rv);
|
||||
@@ -2358,7 +2409,7 @@ nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange,
|
||||
}
|
||||
|
||||
nsINode *ourNode, *otherNode;
|
||||
int32_t ourOffset, otherOffset;
|
||||
uint32_t ourOffset, otherOffset;
|
||||
|
||||
switch (aHow) {
|
||||
case nsIDOMRange::START_TO_START:
|
||||
@@ -2396,8 +2447,10 @@ nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange,
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nsContentUtils::ComparePoints(ourNode, ourOffset,
|
||||
otherNode, otherOffset);
|
||||
return nsContentUtils::ComparePoints(ourNode,
|
||||
static_cast<int32_t>(ourOffset),
|
||||
otherNode,
|
||||
static_cast<int32_t>(otherOffset));
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
@@ -2514,8 +2567,7 @@ nsRange::CloneContents(ErrorResult& aRv)
|
||||
bool deepClone = !node->IsElement() ||
|
||||
(!(node == mEndContainer && mEndOffset == 0) &&
|
||||
!(node == mStartContainer &&
|
||||
mStartOffset ==
|
||||
int32_t(node->AsElement()->GetChildCount())));
|
||||
mStartOffset == node->AsElement()->GetChildCount()));
|
||||
|
||||
// Clone the current subtree!
|
||||
|
||||
@@ -2544,8 +2596,7 @@ nsRange::CloneContents(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (dataLength > (uint32_t)mEndOffset)
|
||||
{
|
||||
if (dataLength > mEndOffset) {
|
||||
aRv = charData->DeleteData(mEndOffset, dataLength - mEndOffset);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
@@ -2702,7 +2753,7 @@ nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t tStartOffset = StartOffset();
|
||||
uint32_t tStartOffset = StartOffset();
|
||||
|
||||
nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv);
|
||||
if (aRv.Failed()) {
|
||||
@@ -2763,18 +2814,20 @@ nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv)
|
||||
// We might need to update the end to include the new node (bug 433662).
|
||||
// Ideally we'd only do this if needed, but it's tricky to know when it's
|
||||
// needed in advance (bug 765799).
|
||||
int32_t newOffset;
|
||||
uint32_t newOffset;
|
||||
|
||||
if (referenceNode) {
|
||||
newOffset = IndexOf(referenceNode);
|
||||
int32_t indexInParent = IndexOf(referenceNode);
|
||||
if (NS_WARN_IF(indexInParent < 0)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
newOffset = static_cast<uint32_t>(indexInParent);
|
||||
} else {
|
||||
uint32_t length;
|
||||
aRv = tChildList->GetLength(&length);
|
||||
aRv = tChildList->GetLength(&newOffset);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
newOffset = length;
|
||||
}
|
||||
|
||||
if (aNode.NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
||||
@@ -3120,11 +3173,16 @@ nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
|
||||
Sequence<nsString>* aTextList,
|
||||
nsRange* aRange,
|
||||
nsINode* aStartContainer,
|
||||
int32_t aStartOffset,
|
||||
uint32_t aStartOffset,
|
||||
nsINode* aEndContainer,
|
||||
int32_t aEndOffset,
|
||||
uint32_t aEndOffset,
|
||||
bool aClampToEdge, bool aFlushLayout)
|
||||
{
|
||||
// Currently, this method is called with start of end offset of nsRange.
|
||||
// So, they must be between 0 - INT32_MAX.
|
||||
MOZ_ASSERT(IsValidOffset(aStartOffset));
|
||||
MOZ_ASSERT(IsValidOffset(aEndOffset));
|
||||
|
||||
// Hold strong pointers across the flush
|
||||
nsCOMPtr<nsINode> startContainer = aStartContainer;
|
||||
nsCOMPtr<nsINode> endContainer = aEndContainer;
|
||||
@@ -3155,13 +3213,15 @@ nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
|
||||
if (textFrame) {
|
||||
int32_t outOffset;
|
||||
nsIFrame* outFrame;
|
||||
textFrame->GetChildFrameContainingOffset(aStartOffset, false,
|
||||
&outOffset, &outFrame);
|
||||
textFrame->GetChildFrameContainingOffset(
|
||||
static_cast<int32_t>(aStartOffset), false,
|
||||
&outOffset, &outFrame);
|
||||
if (outFrame) {
|
||||
nsIFrame* relativeTo =
|
||||
nsLayoutUtils::GetContainingBlockForClientRect(outFrame);
|
||||
nsRect r = outFrame->GetRectRelativeToSelf();
|
||||
ExtractRectFromOffset(outFrame, aStartOffset, &r, false, aClampToEdge);
|
||||
ExtractRectFromOffset(outFrame, static_cast<int32_t>(aStartOffset),
|
||||
&r, false, aClampToEdge);
|
||||
r.width = 0;
|
||||
r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r, relativeTo);
|
||||
aCollector->AddRect(r);
|
||||
@@ -3180,12 +3240,14 @@ nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
|
||||
if (content->IsNodeOfType(nsINode::eTEXT)) {
|
||||
if (node == startContainer) {
|
||||
int32_t offset = startContainer == endContainer ?
|
||||
aEndOffset : content->GetText()->GetLength();
|
||||
GetPartialTextRect(aCollector, aTextList, content, aStartOffset, offset,
|
||||
static_cast<int32_t>(aEndOffset) : content->GetText()->GetLength();
|
||||
GetPartialTextRect(aCollector, aTextList, content,
|
||||
static_cast<int32_t>(aStartOffset), offset,
|
||||
aClampToEdge, aFlushLayout);
|
||||
continue;
|
||||
} else if (node == endContainer) {
|
||||
GetPartialTextRect(aCollector, aTextList, content, 0, aEndOffset,
|
||||
GetPartialTextRect(aCollector, aTextList, content,
|
||||
0, static_cast<int32_t>(aEndOffset),
|
||||
aClampToEdge, aFlushLayout);
|
||||
continue;
|
||||
}
|
||||
@@ -3560,7 +3622,7 @@ ElementIsVisibleNoFlush(Element* aElement)
|
||||
static void
|
||||
AppendTransformedText(InnerTextAccumulator& aResult,
|
||||
nsGenericDOMDataNode* aTextNode,
|
||||
int32_t aStart, int32_t aEnd)
|
||||
uint32_t aStart, uint32_t aEnd)
|
||||
{
|
||||
nsIFrame* frame = aTextNode->GetPrimaryFrame();
|
||||
if (!IsVisibleAndNotInReplacedElement(frame)) {
|
||||
@@ -3669,7 +3731,7 @@ nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError,
|
||||
if (aEndContainer->IsNodeOfType(nsINode::eTEXT)) {
|
||||
endState = AT_NODE;
|
||||
} else {
|
||||
if (uint32_t(aEndOffset) < aEndContainer->GetChildCount()) {
|
||||
if (aEndOffset < aEndContainer->GetChildCount()) {
|
||||
endNode = aEndContainer->GetChildAt(aEndOffset);
|
||||
endState = AT_NODE;
|
||||
}
|
||||
|
||||
@@ -47,14 +47,20 @@ class nsRange final : public nsIDOMRange,
|
||||
public:
|
||||
explicit nsRange(nsINode* aNode);
|
||||
|
||||
static nsresult CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
|
||||
nsIDOMNode* aEndContainer, int32_t aEndOffset,
|
||||
static nsresult CreateRange(nsIDOMNode* aStartContainer,
|
||||
uint32_t aStartOffset,
|
||||
nsIDOMNode* aEndContainer,
|
||||
uint32_t aEndOffset,
|
||||
nsRange** aRange);
|
||||
static nsresult CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
|
||||
nsIDOMNode* aEndContainer, int32_t aEndOffset,
|
||||
static nsresult CreateRange(nsIDOMNode* aStartContainer,
|
||||
uint32_t aStartOffset,
|
||||
nsIDOMNode* aEndContainer,
|
||||
uint32_t aEndOffset,
|
||||
nsIDOMRange** aRange);
|
||||
static nsresult CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
|
||||
nsINode* aEndContainer, int32_t aEndOffset,
|
||||
static nsresult CreateRange(nsINode* aStartContainer,
|
||||
uint32_t aStartOffset,
|
||||
nsINode* aEndContainer,
|
||||
uint32_t aEndOffset,
|
||||
nsRange** aRange);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@@ -83,12 +89,12 @@ public:
|
||||
return mEndContainer;
|
||||
}
|
||||
|
||||
int32_t StartOffset() const
|
||||
uint32_t StartOffset() const
|
||||
{
|
||||
return mStartOffset;
|
||||
}
|
||||
|
||||
int32_t EndOffset() const
|
||||
uint32_t EndOffset() const
|
||||
{
|
||||
return mEndOffset;
|
||||
}
|
||||
@@ -147,8 +153,8 @@ public:
|
||||
* When you set both start and end of a range, you should use
|
||||
* SetStartAndEnd() instead.
|
||||
*/
|
||||
nsresult SetStart(nsINode* aContainer, int32_t aOffset);
|
||||
nsresult SetEnd(nsINode* aContainer, int32_t aOffset);
|
||||
nsresult SetStart(nsINode* aContainer, uint32_t aOffset);
|
||||
nsresult SetEnd(nsINode* aContainer, uint32_t aOffset);
|
||||
|
||||
already_AddRefed<nsRange> CloneRange() const;
|
||||
|
||||
@@ -160,15 +166,15 @@ public:
|
||||
* collapsed at the end point. Similarly, if they are in different root,
|
||||
* the range will be collapsed at the end point.
|
||||
*/
|
||||
nsresult SetStartAndEnd(nsINode* aStartContainer, int32_t aStartOffset,
|
||||
nsINode* aEndContainer, int32_t aEndOffset);
|
||||
nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
|
||||
nsINode* aEndContainer, uint32_t aEndOffset);
|
||||
|
||||
/**
|
||||
* CollapseTo() works similar to call both SetStart() and SetEnd() with
|
||||
* same node and offset. This just calls SetStartAndParent() to set
|
||||
* collapsed range at aContainer and aOffset.
|
||||
*/
|
||||
nsresult CollapseTo(nsINode* aContainer, int32_t aOffset)
|
||||
nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset)
|
||||
{
|
||||
return SetStartAndEnd(aContainer, aOffset, aContainer, aOffset);
|
||||
}
|
||||
@@ -177,23 +183,36 @@ public:
|
||||
* Retrieves node and offset for setting start or end of a range to
|
||||
* before or after aNode.
|
||||
*/
|
||||
static nsINode* GetContainerAndOffsetAfter(nsINode* aNode, int32_t* aOffset)
|
||||
static nsINode* GetContainerAndOffsetAfter(nsINode* aNode, uint32_t* aOffset)
|
||||
{
|
||||
MOZ_ASSERT(aNode);
|
||||
MOZ_ASSERT(aOffset);
|
||||
*aOffset = 0;
|
||||
nsINode* parentNode = aNode->GetParentNode();
|
||||
*aOffset = parentNode ? parentNode->IndexOf(aNode) : -1;
|
||||
if (*aOffset >= 0) {
|
||||
(*aOffset)++;
|
||||
if (!parentNode) {
|
||||
return nullptr;
|
||||
}
|
||||
int32_t indexInParent = parentNode->IndexOf(aNode);
|
||||
if (NS_WARN_IF(indexInParent < 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
*aOffset = static_cast<uint32_t>(indexInParent) + 1;
|
||||
return parentNode;
|
||||
}
|
||||
static nsINode* GetContainerAndOffsetBefore(nsINode* aNode, int32_t* aOffset)
|
||||
static nsINode* GetContainerAndOffsetBefore(nsINode* aNode, uint32_t* aOffset)
|
||||
{
|
||||
MOZ_ASSERT(aNode);
|
||||
MOZ_ASSERT(aOffset);
|
||||
*aOffset = 0;
|
||||
nsINode* parentNode = aNode->GetParentNode();
|
||||
*aOffset = parentNode ? parentNode->IndexOf(aNode) : -1;
|
||||
if (!parentNode) {
|
||||
return nullptr;
|
||||
}
|
||||
int32_t indexInParent = parentNode->IndexOf(aNode);
|
||||
if (NS_WARN_IF(indexInParent < 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
*aOffset = static_cast<uint32_t>(indexInParent);
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
@@ -327,9 +346,9 @@ public:
|
||||
mozilla::dom::Sequence<nsString>* aTextList,
|
||||
nsRange* aRange,
|
||||
nsINode* aStartContainer,
|
||||
int32_t aStartOffset,
|
||||
uint32_t aStartOffset,
|
||||
nsINode* aEndContainer,
|
||||
int32_t aEndOffset,
|
||||
uint32_t aEndOffset,
|
||||
bool aClampToEdge, bool aFlushLayout);
|
||||
|
||||
/**
|
||||
@@ -350,14 +369,25 @@ protected:
|
||||
void RegisterCommonAncestor(nsINode* aNode);
|
||||
void UnregisterCommonAncestor(nsINode* aNode);
|
||||
nsINode* IsValidBoundary(nsINode* aNode);
|
||||
static bool IsValidOffset(nsINode* aNode, int32_t aOffset);
|
||||
|
||||
/**
|
||||
* XXX nsRange should accept 0 - UINT32_MAX as offset. However, users of
|
||||
* nsRange treat offset as int32_t. Additionally, some other internal
|
||||
* APIs like nsINode::IndexOf() use int32_t. Therefore, nsRange should
|
||||
* accept only 0 - INT32_MAX as valid offset for now.
|
||||
*/
|
||||
static bool IsValidOffset(uint32_t aOffset)
|
||||
{
|
||||
return aOffset <= INT32_MAX;
|
||||
}
|
||||
static bool IsValidOffset(nsINode* aNode, uint32_t aOffset);
|
||||
|
||||
// CharacterDataChanged set aNotInsertedYet to true to disable an assertion
|
||||
// and suppress re-registering a range common ancestor node since
|
||||
// the new text node of a splitText hasn't been inserted yet.
|
||||
// CharacterDataChanged does the re-registering when needed.
|
||||
void DoSetRange(nsINode* aStartN, int32_t aStartOffset,
|
||||
nsINode* aEndN, int32_t aEndOffset,
|
||||
void DoSetRange(nsINode* aStartN, uint32_t aStartOffset,
|
||||
nsINode* aEndN, uint32_t aEndOffset,
|
||||
nsINode* aRoot, bool aNotInsertedYet = false);
|
||||
|
||||
/**
|
||||
@@ -430,8 +460,8 @@ protected:
|
||||
nsCOMPtr<nsINode> mStartContainer;
|
||||
nsCOMPtr<nsINode> mEndContainer;
|
||||
RefPtr<mozilla::dom::Selection> mSelection;
|
||||
int32_t mStartOffset;
|
||||
int32_t mEndOffset;
|
||||
uint32_t mStartOffset;
|
||||
uint32_t mEndOffset;
|
||||
|
||||
bool mIsPositioned : 1;
|
||||
bool mMaySpanAnonymousSubtrees : 1;
|
||||
|
||||
@@ -505,20 +505,23 @@ GamepadManager::Update(const GamepadChangeEvent& aEvent)
|
||||
return;
|
||||
}
|
||||
|
||||
if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
|
||||
const GamepadAdded& a = aEvent.get_GamepadAdded();
|
||||
AddGamepad(a.index(), a.id(),
|
||||
const uint32_t index = aEvent.index();
|
||||
GamepadServiceType serviceType = aEvent.service_type();
|
||||
GamepadChangeEventBody body = aEvent.body();
|
||||
|
||||
if (body.type() == GamepadChangeEventBody::TGamepadAdded) {
|
||||
const GamepadAdded& a = body.get_GamepadAdded();
|
||||
AddGamepad(index, a.id(),
|
||||
static_cast<GamepadMappingType>(a.mapping()),
|
||||
static_cast<GamepadHand>(a.hand()),
|
||||
a.service_type(),
|
||||
serviceType,
|
||||
a.display_id(),
|
||||
a.num_buttons(), a.num_axes(),
|
||||
a.num_haptics());
|
||||
return;
|
||||
}
|
||||
if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
|
||||
const GamepadRemoved& a = aEvent.get_GamepadRemoved();
|
||||
RemoveGamepad(a.index(), a.service_type());
|
||||
if (body.type() == GamepadChangeEventBody::TGamepadRemoved) {
|
||||
RemoveGamepad(index, serviceType);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -555,96 +558,75 @@ GamepadManager::MaybeConvertToNonstandardGamepadEvent(const GamepadChangeEvent&
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Gamepad> gamepad;
|
||||
RefPtr<Gamepad> gamepad = aWindow->GetGamepad(aEvent.index());
|
||||
const GamepadChangeEventBody& body = aEvent.body();
|
||||
|
||||
switch (aEvent.type()) {
|
||||
case GamepadChangeEvent::TGamepadButtonInformation:
|
||||
if (gamepad) {
|
||||
switch (body.type()) {
|
||||
case GamepadChangeEventBody::TGamepadButtonInformation:
|
||||
{
|
||||
const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
|
||||
gamepad = aWindow->GetGamepad(a.index());
|
||||
if (gamepad) {
|
||||
FireButtonEvent(aWindow, gamepad, a.button(), a.value());
|
||||
}
|
||||
const GamepadButtonInformation& a = body.get_GamepadButtonInformation();
|
||||
FireButtonEvent(aWindow, gamepad, a.button(), a.value());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GamepadChangeEvent::TGamepadAxisInformation:
|
||||
case GamepadChangeEventBody::TGamepadAxisInformation:
|
||||
{
|
||||
const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
|
||||
gamepad = aWindow->GetGamepad(a.index());
|
||||
if (gamepad) {
|
||||
FireAxisMoveEvent(aWindow, gamepad, a.axis(), a.value());
|
||||
}
|
||||
const GamepadAxisInformation& a = body.get_GamepadAxisInformation();
|
||||
FireAxisMoveEvent(aWindow, gamepad, a.axis(), a.value());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GamepadManager::SetGamepadByEvent(const GamepadChangeEvent& aEvent, nsGlobalWindow *aWindow)
|
||||
{
|
||||
uint32_t index;
|
||||
RefPtr<Gamepad> gamepad;
|
||||
bool ret = false;
|
||||
bool firstTime = false;
|
||||
|
||||
switch (aEvent.type()) {
|
||||
case GamepadChangeEvent::TGamepadButtonInformation:
|
||||
{
|
||||
const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
|
||||
index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
|
||||
if (aWindow) {
|
||||
firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
|
||||
}
|
||||
gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
|
||||
if (gamepad) {
|
||||
const uint32_t index = GetGamepadIndexWithServiceType(aEvent.index(),
|
||||
aEvent.service_type());
|
||||
if (aWindow) {
|
||||
firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
|
||||
}
|
||||
|
||||
RefPtr<Gamepad> gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
|
||||
const GamepadChangeEventBody& body = aEvent.body();
|
||||
|
||||
if (gamepad) {
|
||||
switch (body.type()) {
|
||||
case GamepadChangeEventBody::TGamepadButtonInformation:
|
||||
{
|
||||
const GamepadButtonInformation& a = body.get_GamepadButtonInformation();
|
||||
gamepad->SetButton(a.button(), a.pressed(), a.touched(), a.value());
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case GamepadChangeEvent::TGamepadAxisInformation:
|
||||
{
|
||||
const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
|
||||
index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
|
||||
if (aWindow) {
|
||||
firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
|
||||
}
|
||||
gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
|
||||
if (gamepad) {
|
||||
case GamepadChangeEventBody::TGamepadAxisInformation:
|
||||
{
|
||||
const GamepadAxisInformation& a = body.get_GamepadAxisInformation();
|
||||
gamepad->SetAxis(a.axis(), a.value());
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case GamepadChangeEvent::TGamepadPoseInformation:
|
||||
{
|
||||
const GamepadPoseInformation& a = aEvent.get_GamepadPoseInformation();
|
||||
index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
|
||||
if (aWindow) {
|
||||
firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
|
||||
}
|
||||
gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
|
||||
if (gamepad) {
|
||||
case GamepadChangeEventBody::TGamepadPoseInformation:
|
||||
{
|
||||
const GamepadPoseInformation& a = body.get_GamepadPoseInformation();
|
||||
gamepad->SetPose(a.pose_state());
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case GamepadChangeEvent::TGamepadHandInformation:
|
||||
{
|
||||
const GamepadHandInformation& a = aEvent.get_GamepadHandInformation();
|
||||
index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
|
||||
if (aWindow) {
|
||||
firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
|
||||
}
|
||||
gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
|
||||
if (gamepad) {
|
||||
case GamepadChangeEventBody::TGamepadHandInformation:
|
||||
{
|
||||
const GamepadHandInformation& a = body.get_GamepadHandInformation();
|
||||
gamepad->SetHand(a.hand());
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (aWindow && firstTime) {
|
||||
|
||||
@@ -58,14 +58,15 @@ GamepadPlatformService::GetParentService()
|
||||
|
||||
template<class T>
|
||||
void
|
||||
GamepadPlatformService::NotifyGamepadChange(const T& aInfo)
|
||||
GamepadPlatformService::NotifyGamepadChange(uint32_t aIndex, const T& aInfo)
|
||||
{
|
||||
// This method is called by monitor populated in
|
||||
// platform-dependent backends
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
GamepadChangeEvent e(aInfo);
|
||||
GamepadChangeEventBody body(aInfo);
|
||||
GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body);
|
||||
|
||||
// mChannelParents may be accessed by background thread in the
|
||||
// same time, we use mutex to prevent possible race condtion
|
||||
@@ -96,12 +97,12 @@ GamepadPlatformService::AddGamepad(const char* aID,
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
uint32_t index = ++mGamepadIndex;
|
||||
// Only VR controllers has displayID, we give 0 to the general gamepads.
|
||||
GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index,
|
||||
aMapping, aHand, GamepadServiceType::Standard,
|
||||
0, aNumButtons, aNumAxes, aHaptics);
|
||||
|
||||
NotifyGamepadChange<GamepadAdded>(a);
|
||||
// Only VR controllers has displayID, we give 0 to the general gamepads.
|
||||
GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)),
|
||||
aMapping, aHand, 0, aNumButtons, aNumAxes, aHaptics);
|
||||
|
||||
NotifyGamepadChange<GamepadAdded>(index, a);
|
||||
return index;
|
||||
}
|
||||
|
||||
@@ -112,8 +113,8 @@ GamepadPlatformService::RemoveGamepad(uint32_t aIndex)
|
||||
// platform-dependent backends
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
GamepadRemoved a(aIndex, GamepadServiceType::Standard);
|
||||
NotifyGamepadChange<GamepadRemoved>(a);
|
||||
GamepadRemoved a;
|
||||
NotifyGamepadChange<GamepadRemoved>(aIndex, a);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -125,9 +126,8 @@ GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
|
||||
// platform-dependent backends
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
GamepadButtonInformation a(aIndex, GamepadServiceType::Standard,
|
||||
aButton, aValue, aPressed, aTouched);
|
||||
NotifyGamepadChange<GamepadButtonInformation>(a);
|
||||
GamepadButtonInformation a(aButton, aValue, aPressed, aTouched);
|
||||
NotifyGamepadChange<GamepadButtonInformation>(aIndex, a);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -162,9 +162,8 @@ GamepadPlatformService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
|
||||
// platform-dependent backends
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
GamepadAxisInformation a(aIndex, GamepadServiceType::Standard,
|
||||
aAxis, aValue);
|
||||
NotifyGamepadChange<GamepadAxisInformation>(a);
|
||||
GamepadAxisInformation a(aAxis, aValue);
|
||||
NotifyGamepadChange<GamepadAxisInformation>(aIndex, a);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -175,9 +174,8 @@ GamepadPlatformService::NewPoseEvent(uint32_t aIndex,
|
||||
// platform-dependent backends
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
GamepadPoseInformation a(aIndex, GamepadServiceType::Standard,
|
||||
aPose);
|
||||
NotifyGamepadChange<GamepadPoseInformation>(a);
|
||||
GamepadPoseInformation a(aPose);
|
||||
NotifyGamepadChange<GamepadPoseInformation>(aIndex, a);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -81,7 +81,7 @@ class GamepadPlatformService final
|
||||
private:
|
||||
GamepadPlatformService();
|
||||
~GamepadPlatformService();
|
||||
template<class T> void NotifyGamepadChange(const T& aInfo);
|
||||
template<class T> void NotifyGamepadChange(uint32_t aIndex, const T& aInfo);
|
||||
|
||||
// Flush all pending events buffered in mPendingEvents, must be called
|
||||
// with mMutex held
|
||||
|
||||
@@ -124,11 +124,11 @@ GamepadServiceTest::AddGamepad(const nsAString& aID,
|
||||
}
|
||||
|
||||
// Only VR controllers has displayID, we give 0 to the general gamepads.
|
||||
GamepadAdded a(nsString(aID), 0,
|
||||
aMapping, aHand,
|
||||
GamepadServiceType::Standard, 0,
|
||||
GamepadAdded a(nsString(aID),
|
||||
aMapping, aHand, 0,
|
||||
aNumButtons, aNumAxes, aNumHaptics);
|
||||
GamepadChangeEvent e(a);
|
||||
GamepadChangeEventBody body(a);
|
||||
GamepadChangeEvent e(0, GamepadServiceType::Standard, body);
|
||||
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
|
||||
|
||||
RefPtr<Promise> p = Promise::Create(go, aRv);
|
||||
@@ -154,8 +154,9 @@ GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
GamepadRemoved a(aIndex, GamepadServiceType::Standard);
|
||||
GamepadChangeEvent e(a);
|
||||
GamepadRemoved a;
|
||||
GamepadChangeEventBody body(a);
|
||||
GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body);
|
||||
|
||||
uint32_t id = ++mEventNumber;
|
||||
if (mChild) {
|
||||
@@ -176,9 +177,9 @@ GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
|
||||
return;
|
||||
}
|
||||
|
||||
GamepadButtonInformation a(aIndex, GamepadServiceType::Standard,
|
||||
aButton, aPressed ? 1.0 : 0, aPressed, aTouched);
|
||||
GamepadChangeEvent e(a);
|
||||
GamepadButtonInformation a(aButton, aPressed ? 1.0 : 0, aPressed, aTouched);
|
||||
GamepadChangeEventBody body(a);
|
||||
GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body);
|
||||
|
||||
uint32_t id = ++mEventNumber;
|
||||
if (mChild) {
|
||||
@@ -200,9 +201,9 @@ GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
|
||||
return;
|
||||
}
|
||||
|
||||
GamepadButtonInformation a(aIndex, GamepadServiceType::Standard,
|
||||
aButton, aValue, aPressed, aTouched);
|
||||
GamepadChangeEvent e(a);
|
||||
GamepadButtonInformation a(aButton, aValue, aPressed, aTouched);
|
||||
GamepadChangeEventBody body(a);
|
||||
GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body);
|
||||
|
||||
uint32_t id = ++mEventNumber;
|
||||
if (mChild) {
|
||||
@@ -222,9 +223,9 @@ GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
|
||||
return;
|
||||
}
|
||||
|
||||
GamepadAxisInformation a(aIndex, GamepadServiceType::Standard,
|
||||
aAxis, aValue);
|
||||
GamepadChangeEvent e(a);
|
||||
GamepadAxisInformation a(aAxis, aValue);
|
||||
GamepadChangeEventBody body(a);
|
||||
GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body);
|
||||
|
||||
uint32_t id = ++mEventNumber;
|
||||
if (mChild) {
|
||||
@@ -305,9 +306,9 @@ GamepadServiceTest::NewPoseMove(uint32_t aIndex,
|
||||
poseState.linearAcceleration[2] = value.Data()[2];
|
||||
}
|
||||
|
||||
GamepadPoseInformation a(aIndex, GamepadServiceType::Standard,
|
||||
poseState);
|
||||
GamepadChangeEvent e(a);
|
||||
GamepadPoseInformation a(poseState);
|
||||
GamepadChangeEventBody body(a);
|
||||
GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body);
|
||||
|
||||
uint32_t id = ++mEventNumber;
|
||||
if (mChild) {
|
||||
|
||||
@@ -13,31 +13,22 @@ namespace dom {
|
||||
|
||||
struct GamepadAdded {
|
||||
nsString id;
|
||||
uint32_t index;
|
||||
GamepadMappingType mapping;
|
||||
GamepadHand hand;
|
||||
GamepadServiceType service_type;
|
||||
uint32_t display_id;
|
||||
uint32_t num_buttons;
|
||||
uint32_t num_axes;
|
||||
uint32_t num_haptics;
|
||||
};
|
||||
|
||||
struct GamepadRemoved {
|
||||
uint32_t index;
|
||||
GamepadServiceType service_type;
|
||||
};
|
||||
struct GamepadRemoved {};
|
||||
|
||||
struct GamepadAxisInformation {
|
||||
uint32_t index;
|
||||
GamepadServiceType service_type;
|
||||
uint32_t axis;
|
||||
double value;
|
||||
};
|
||||
|
||||
struct GamepadButtonInformation {
|
||||
uint32_t index;
|
||||
GamepadServiceType service_type;
|
||||
uint32_t button;
|
||||
double value;
|
||||
bool pressed;
|
||||
@@ -45,18 +36,14 @@ struct GamepadButtonInformation {
|
||||
};
|
||||
|
||||
struct GamepadPoseInformation {
|
||||
uint32_t index;
|
||||
GamepadServiceType service_type;
|
||||
GamepadPoseState pose_state;
|
||||
};
|
||||
|
||||
struct GamepadHandInformation {
|
||||
uint32_t index;
|
||||
GamepadServiceType service_type;
|
||||
GamepadHand hand;
|
||||
};
|
||||
|
||||
union GamepadChangeEvent {
|
||||
union GamepadChangeEventBody {
|
||||
GamepadAdded;
|
||||
GamepadRemoved;
|
||||
GamepadAxisInformation;
|
||||
@@ -65,5 +52,11 @@ union GamepadChangeEvent {
|
||||
GamepadHandInformation;
|
||||
};
|
||||
|
||||
struct GamepadChangeEvent {
|
||||
uint32_t index;
|
||||
GamepadServiceType service_type;
|
||||
GamepadChangeEventBody body;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -18,8 +18,10 @@ GamepadTestChannelParent::RecvGamepadTestEvent(const uint32_t& aID,
|
||||
RefPtr<GamepadPlatformService> service =
|
||||
GamepadPlatformService::GetParentService();
|
||||
MOZ_ASSERT(service);
|
||||
if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
|
||||
const GamepadAdded& a = aEvent.get_GamepadAdded();
|
||||
const uint32_t index = aEvent.index();
|
||||
const GamepadChangeEventBody& body = aEvent.body();
|
||||
if (body.type() == GamepadChangeEventBody::TGamepadAdded) {
|
||||
const GamepadAdded& a = body.get_GamepadAdded();
|
||||
nsCString gamepadID;
|
||||
LossyCopyUTF16toASCII(a.id(), gamepadID);
|
||||
uint32_t index = service->AddGamepad(gamepadID.get(),
|
||||
@@ -33,25 +35,24 @@ GamepadTestChannelParent::RecvGamepadTestEvent(const uint32_t& aID,
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
|
||||
const GamepadRemoved& a = aEvent.get_GamepadRemoved();
|
||||
service->RemoveGamepad(a.index());
|
||||
if (body.type() == GamepadChangeEventBody::TGamepadRemoved) {
|
||||
service->RemoveGamepad(index);
|
||||
return IPC_OK();
|
||||
}
|
||||
if (aEvent.type() == GamepadChangeEvent::TGamepadButtonInformation) {
|
||||
const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
|
||||
service->NewButtonEvent(a.index(), a.button(), a.pressed(), a.touched(),
|
||||
if (body.type() == GamepadChangeEventBody::TGamepadButtonInformation) {
|
||||
const GamepadButtonInformation& a = body.get_GamepadButtonInformation();
|
||||
service->NewButtonEvent(index, a.button(), a.pressed(), a.touched(),
|
||||
a.value());
|
||||
return IPC_OK();
|
||||
}
|
||||
if (aEvent.type() == GamepadChangeEvent::TGamepadAxisInformation) {
|
||||
const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
|
||||
service->NewAxisMoveEvent(a.index(), a.axis(), a.value());
|
||||
if (body.type() == GamepadChangeEventBody::TGamepadAxisInformation) {
|
||||
const GamepadAxisInformation& a = body.get_GamepadAxisInformation();
|
||||
service->NewAxisMoveEvent(index, a.axis(), a.value());
|
||||
return IPC_OK();
|
||||
}
|
||||
if (aEvent.type() == GamepadChangeEvent::TGamepadPoseInformation) {
|
||||
const GamepadPoseInformation& a = aEvent.get_GamepadPoseInformation();
|
||||
service->NewPoseEvent(a.index(), a.pose_state());
|
||||
if (body.type() == GamepadChangeEventBody::TGamepadPoseInformation) {
|
||||
const GamepadPoseInformation& a = body.get_GamepadPoseInformation();
|
||||
service->NewPoseEvent(index, a.pose_state());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
interface nsIDOMRange : nsISupports
|
||||
{
|
||||
readonly attribute nsIDOMNode startContainer;
|
||||
readonly attribute long startOffset;
|
||||
readonly attribute unsigned long startOffset;
|
||||
readonly attribute nsIDOMNode endContainer;
|
||||
readonly attribute long endOffset;
|
||||
readonly attribute unsigned long endOffset;
|
||||
readonly attribute boolean collapsed;
|
||||
readonly attribute nsIDOMNode commonAncestorContainer;
|
||||
|
||||
void setStart(in nsIDOMNode refNode, in long offset);
|
||||
void setEnd(in nsIDOMNode refNode, in long offset);
|
||||
void setStart(in nsIDOMNode refNode, in unsigned long offset);
|
||||
void setEnd(in nsIDOMNode refNode, in unsigned long offset);
|
||||
void setStartBefore(in nsIDOMNode refNode);
|
||||
void setStartAfter(in nsIDOMNode refNode);
|
||||
void setEndBefore(in nsIDOMNode refNode);
|
||||
@@ -56,14 +56,14 @@ interface nsIDOMRange : nsISupports
|
||||
// This returns true if parent+offset equals either
|
||||
// of the boundary points or is between them.
|
||||
boolean isPointInRange(in nsIDOMNode parent,
|
||||
in long offset);
|
||||
in unsigned long offset);
|
||||
|
||||
// comparePoint returns
|
||||
// -1 if point is before the start boundary point,
|
||||
// 0 if point is either of the boundary points or between them,
|
||||
// 1 if point is after the end boundary point.
|
||||
// Sort of a strcmp for ranges.
|
||||
short comparePoint(in nsIDOMNode parent, in long offset);
|
||||
short comparePoint(in nsIDOMNode parent, in unsigned long offset);
|
||||
|
||||
/**
|
||||
* Returns whether the range intersects node.
|
||||
|
||||
@@ -1011,8 +1011,9 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
||||
nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
|
||||
showInfo = ShowInfo(EmptyString(), false,
|
||||
context->UsePrivateBrowsing(), true, false,
|
||||
aTabOpener->mDPI, aTabOpener->mRounding,
|
||||
aTabOpener->mDefaultScale);
|
||||
aTabOpener->WebWidget()->GetDPI(),
|
||||
aTabOpener->WebWidget()->RoundsWidgetCoordinatesTo(),
|
||||
aTabOpener->WebWidget()->GetDefaultScale().scale);
|
||||
}
|
||||
|
||||
newChild->SetMaxTouchPoints(maxTouchPoints);
|
||||
|
||||
@@ -87,6 +87,7 @@ struct ScreenDetails {
|
||||
int32_t colorDepth;
|
||||
DesktopToLayoutDeviceScale contentsScaleFactor;
|
||||
CSSToLayoutDeviceScale defaultCSSScaleFactor;
|
||||
float dpi;
|
||||
};
|
||||
|
||||
struct DimensionInfo
|
||||
|
||||
@@ -359,21 +359,6 @@ parent:
|
||||
|
||||
sync IsParentWindowMainWidgetVisible() returns (bool visible);
|
||||
|
||||
/**
|
||||
* Gets the DPI of the screen corresponding to this browser.
|
||||
*/
|
||||
sync GetDPI() returns (float value);
|
||||
|
||||
/**
|
||||
* Gets the default scaling factor of the screen corresponding to this browser.
|
||||
*/
|
||||
sync GetDefaultScale() returns (double value);
|
||||
|
||||
/**
|
||||
* Gets the rounding of coordinates in the widget.
|
||||
*/
|
||||
sync GetWidgetRounding() returns (int32_t value);
|
||||
|
||||
/**
|
||||
* Set the native cursor.
|
||||
* @param value
|
||||
|
||||
@@ -403,9 +403,6 @@ TabChild::TabChild(nsIContentChild* aManager,
|
||||
, mHasValidInnerSize(false)
|
||||
, mDestroyed(false)
|
||||
, mUniqueId(aTabId)
|
||||
, mDPI(0)
|
||||
, mRounding(0)
|
||||
, mDefaultScale(0)
|
||||
, mIsTransparent(false)
|
||||
, mIPCOpen(false)
|
||||
, mParentIsActive(false)
|
||||
@@ -1148,6 +1145,15 @@ TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
void
|
||||
TabChild::ApplyShowInfo(const ShowInfo& aInfo)
|
||||
{
|
||||
// Even if we already set real show info, the dpi / rounding & scale may still
|
||||
// be invalid (if TabParent wasn't able to get widget it would just send 0).
|
||||
// So better to always set up-to-date values here.
|
||||
if (aInfo.dpi() > 0) {
|
||||
mPuppetWidget->UpdateBackingScaleCache(aInfo.dpi(),
|
||||
aInfo.widgetRounding(),
|
||||
aInfo.defaultScale());
|
||||
}
|
||||
|
||||
if (mDidSetRealShowInfo) {
|
||||
return;
|
||||
}
|
||||
@@ -1188,9 +1194,6 @@ TabChild::ApplyShowInfo(const ShowInfo& aInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
mDPI = aInfo.dpi();
|
||||
mRounding = aInfo.widgetRounding();
|
||||
mDefaultScale = aInfo.defaultScale();
|
||||
mIsTransparent = aInfo.isTransparent();
|
||||
}
|
||||
|
||||
@@ -2704,56 +2707,6 @@ TabChild::InitAPZState()
|
||||
cbc->SendPAPZConstructor(apzChild, mLayersId);
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::GetDPI(float* aDPI)
|
||||
{
|
||||
*aDPI = -1.0;
|
||||
if (!(mDidFakeShow || mDidSetRealShowInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDPI > 0) {
|
||||
*aDPI = mDPI;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to a sync call if needed.
|
||||
SendGetDPI(aDPI);
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::GetDefaultScale(double* aScale)
|
||||
{
|
||||
*aScale = -1.0;
|
||||
if (!(mDidFakeShow || mDidSetRealShowInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDefaultScale > 0) {
|
||||
*aScale = mDefaultScale;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to a sync call if needed.
|
||||
SendGetDefaultScale(aScale);
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::GetWidgetRounding(int32_t* aRounding)
|
||||
{
|
||||
*aRounding = 1;
|
||||
if (!(mDidFakeShow || mDidSetRealShowInfo)) {
|
||||
return;
|
||||
}
|
||||
if (mRounding > 0) {
|
||||
*aRounding = mRounding;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to a sync call if needed.
|
||||
SendGetWidgetRounding(aRounding);
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::NotifyPainted()
|
||||
{
|
||||
@@ -3151,10 +3104,9 @@ TabChild::RecvUIResolutionChanged(const float& aDpi,
|
||||
const double& aScale)
|
||||
{
|
||||
ScreenIntSize oldScreenSize = GetInnerSize();
|
||||
mDPI = 0;
|
||||
mRounding = 0;
|
||||
mDefaultScale = 0;
|
||||
static_cast<PuppetWidget*>(mPuppetWidget.get())->UpdateBackingScaleCache(aDpi, aRounding, aScale);
|
||||
if (aDpi > 0) {
|
||||
mPuppetWidget->UpdateBackingScaleCache(aDpi, aRounding, aScale);
|
||||
}
|
||||
nsCOMPtr<nsIDocument> document(GetDocument());
|
||||
nsCOMPtr<nsIPresShell> presShell = document->GetShell();
|
||||
if (presShell) {
|
||||
|
||||
@@ -502,13 +502,6 @@ public:
|
||||
|
||||
virtual PuppetWidget* WebWidget() override { return mPuppetWidget; }
|
||||
|
||||
/** Return the DPI of the widget this TabChild draws to. */
|
||||
void GetDPI(float* aDPI);
|
||||
|
||||
void GetDefaultScale(double *aScale);
|
||||
|
||||
void GetWidgetRounding(int32_t* aRounding);
|
||||
|
||||
bool IsTransparent() const { return mIsTransparent; }
|
||||
|
||||
void GetMaxTouchPoints(uint32_t* aTouchPoints)
|
||||
@@ -855,9 +848,6 @@ private:
|
||||
Maybe<mozilla::layers::CompositorOptions> mCompositorOptions;
|
||||
|
||||
friend class ContentChild;
|
||||
float mDPI;
|
||||
int32_t mRounding;
|
||||
double mDefaultScale;
|
||||
|
||||
bool mIsTransparent;
|
||||
|
||||
|
||||
@@ -2350,39 +2350,6 @@ TabParent::RecvIsParentWindowMainWidgetVisible(bool* aIsVisible)
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvGetDPI(float* aValue)
|
||||
{
|
||||
TryCacheDPIAndScale();
|
||||
|
||||
MOZ_ASSERT(mDPI > 0 || mFrameElement,
|
||||
"Must not ask for DPI before OwnerElement is received!");
|
||||
*aValue = mDPI;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvGetDefaultScale(double* aValue)
|
||||
{
|
||||
TryCacheDPIAndScale();
|
||||
|
||||
MOZ_ASSERT(mDefaultScale.scale > 0 || mFrameElement,
|
||||
"Must not ask for scale before OwnerElement is received!");
|
||||
*aValue = mDefaultScale.scale;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvGetWidgetRounding(int32_t* aValue)
|
||||
{
|
||||
TryCacheDPIAndScale();
|
||||
|
||||
MOZ_ASSERT(mRounding > 0 || mFrameElement,
|
||||
"Must not ask for rounding before OwnerElement is received!");
|
||||
*aValue = mRounding;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIWidget>
|
||||
TabParent::GetTopLevelWidget()
|
||||
{
|
||||
|
||||
@@ -299,11 +299,6 @@ public:
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvHideTooltip() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvGetDPI(float* aValue) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvGetDefaultScale(double* aValue) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvGetWidgetRounding(int32_t* aValue) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override;
|
||||
|
||||
|
||||
@@ -457,7 +457,9 @@ IsAACCodecString(const nsAString& aCodec)
|
||||
{
|
||||
return
|
||||
aCodec.EqualsLiteral("mp4a.40.2") || // MPEG4 AAC-LC
|
||||
aCodec.EqualsLiteral("mp4a.40.02") || // MPEG4 AAC-LC(for compatibility)
|
||||
aCodec.EqualsLiteral("mp4a.40.5") || // MPEG4 HE-AAC
|
||||
aCodec.EqualsLiteral("mp4a.40.05") || // MPEG4 HE-AAC(for compatibility)
|
||||
aCodec.EqualsLiteral("mp4a.67") || // MPEG2 AAC-LC
|
||||
aCodec.EqualsLiteral("mp4a.40.29"); // MPEG4 HE-AACv2
|
||||
}
|
||||
|
||||
@@ -324,11 +324,28 @@ private:
|
||||
* a constructor. Please add below (UniquePtr covers a lot of ground though).
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
class Refcountable : public T
|
||||
class RefcountableBase
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefcountableBase)
|
||||
protected:
|
||||
virtual ~RefcountableBase() {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Refcountable : public T, public RefcountableBase
|
||||
{
|
||||
public:
|
||||
NS_METHOD_(MozExternalRefCountType) AddRef()
|
||||
{
|
||||
return RefcountableBase::AddRef();
|
||||
}
|
||||
|
||||
NS_METHOD_(MozExternalRefCountType) Release()
|
||||
{
|
||||
return RefcountableBase::Release();
|
||||
}
|
||||
|
||||
private:
|
||||
~Refcountable<T>() {}
|
||||
};
|
||||
|
||||
@@ -1057,7 +1057,6 @@ skip-if = android_version == '17' # android(bug 1232305)
|
||||
[test_standalone.html]
|
||||
skip-if = toolkit == 'android' # bug 1372457
|
||||
[test_streams_autoplay.html]
|
||||
skip-if = toolkit == 'android' # bug 1372457
|
||||
tags=msg capturestream
|
||||
[test_streams_capture_origin.html]
|
||||
skip-if = toolkit == 'android' # bug 1372457
|
||||
|
||||
@@ -354,7 +354,7 @@ public:
|
||||
void GetSettings(dom::MediaTrackSettings& aOutSettings)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aOutSettings = mSettings;
|
||||
aOutSettings = *mSettings;
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -362,6 +362,7 @@ protected:
|
||||
explicit MediaEngineSource(MediaEngineState aState)
|
||||
: mState(aState)
|
||||
, mInShutdown(false)
|
||||
, mSettings(MakeRefPtr<media::Refcountable<dom::MediaTrackSettings>>())
|
||||
{}
|
||||
|
||||
/* UpdateSingleSource - Centralized abstract function to implement in those
|
||||
@@ -448,8 +449,10 @@ protected:
|
||||
nsTArray<RefPtr<AllocationHandle>> mRegisteredHandles;
|
||||
bool mInShutdown;
|
||||
|
||||
// Main-thread only:
|
||||
dom::MediaTrackSettings mSettings;
|
||||
// The following is accessed on main-thread only. It has its own ref-count to
|
||||
// avoid ref-counting MediaEngineSource itself in runnables.
|
||||
// (MediaEngineSource subclasses balk on ref-counts too late during shutdown.)
|
||||
RefPtr<media::Refcountable<dom::MediaTrackSettings>> mSettings;
|
||||
};
|
||||
|
||||
class MediaEngineVideoSource : public MediaEngineSource
|
||||
|
||||
@@ -60,8 +60,6 @@ public:
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
||||
const nsString& aDeviceId) const override;
|
||||
|
||||
void Shutdown() override {};
|
||||
|
||||
protected:
|
||||
struct CapabilityCandidate {
|
||||
explicit CapabilityCandidate(uint8_t index, uint32_t distance = 0)
|
||||
|
||||
@@ -36,9 +36,9 @@ MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource(
|
||||
mScary(aScary)
|
||||
{
|
||||
MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other);
|
||||
mSettings.mWidth.Construct(0);
|
||||
mSettings.mHeight.Construct(0);
|
||||
mSettings.mFrameRate.Construct(0);
|
||||
mSettings->mWidth.Construct(0);
|
||||
mSettings->mHeight.Construct(0);
|
||||
mSettings->mFrameRate.Construct(0);
|
||||
Init();
|
||||
}
|
||||
|
||||
@@ -328,12 +328,12 @@ MediaEngineRemoteVideoSource::SetLastCapability(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
RefPtr<MediaEngineRemoteVideoSource> that = this;
|
||||
auto settings = mSettings;
|
||||
|
||||
NS_DispatchToMainThread(media::NewRunnableFrom([that, cap]() mutable {
|
||||
that->mSettings.mWidth.Value() = cap.width;
|
||||
that->mSettings.mHeight.Value() = cap.height;
|
||||
that->mSettings.mFrameRate.Value() = cap.maxFPS;
|
||||
NS_DispatchToMainThread(media::NewRunnableFrom([settings, cap]() mutable {
|
||||
settings->mWidth.Value() = cap.width;
|
||||
settings->mHeight.Value() = cap.height;
|
||||
settings->mFrameRate.Value() = cap.maxFPS;
|
||||
return NS_OK;
|
||||
}));
|
||||
}
|
||||
@@ -371,10 +371,10 @@ MediaEngineRemoteVideoSource::FrameSizeChange(unsigned int w, unsigned int h)
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
|
||||
RefPtr<MediaEngineRemoteVideoSource> that = this;
|
||||
NS_DispatchToMainThread(media::NewRunnableFrom([that, w, h]() mutable {
|
||||
that->mSettings.mWidth.Value() = w;
|
||||
that->mSettings.mHeight.Value() = h;
|
||||
auto settings = mSettings;
|
||||
NS_DispatchToMainThread(media::NewRunnableFrom([settings, w, h]() mutable {
|
||||
settings->mWidth.Value() = w;
|
||||
settings->mHeight.Value() = h;
|
||||
return NS_OK;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -198,10 +198,10 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
|
||||
mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
|
||||
mDeviceUUID.Assign(uuid);
|
||||
mListener = new mozilla::WebRTCAudioDataListener(this);
|
||||
mSettings.mEchoCancellation.Construct(0);
|
||||
mSettings.mAutoGainControl.Construct(0);
|
||||
mSettings.mNoiseSuppression.Construct(0);
|
||||
mSettings.mChannelCount.Construct(0);
|
||||
mSettings->mEchoCancellation.Construct(0);
|
||||
mSettings->mAutoGainControl.Construct(0);
|
||||
mSettings->mNoiseSuppression.Construct(0);
|
||||
mSettings->mChannelCount.Construct(0);
|
||||
// We'll init lazily as needed
|
||||
}
|
||||
|
||||
@@ -417,10 +417,10 @@ MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
|
||||
RefPtr<MediaEngineWebRTCMicrophoneSource> that = this;
|
||||
|
||||
NS_DispatchToMainThread(media::NewRunnableFrom([that, aPrefs]() mutable {
|
||||
that->mSettings.mEchoCancellation.Value() = aPrefs.mAecOn;
|
||||
that->mSettings.mAutoGainControl.Value() = aPrefs.mAgcOn;
|
||||
that->mSettings.mNoiseSuppression.Value() = aPrefs.mNoiseOn;
|
||||
that->mSettings.mChannelCount.Value() = aPrefs.mChannels;
|
||||
that->mSettings->mEchoCancellation.Value() = aPrefs.mAecOn;
|
||||
that->mSettings->mAutoGainControl.Value() = aPrefs.mAgcOn;
|
||||
that->mSettings->mNoiseSuppression.Value() = aPrefs.mNoiseOn;
|
||||
that->mSettings->mChannelCount.Value() = aPrefs.mChannels;
|
||||
return NS_OK;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -59,10 +59,6 @@ dictionary RTCOfferOptions : RTCOfferAnswerOptions {
|
||||
long offerToReceiveVideo;
|
||||
long offerToReceiveAudio;
|
||||
boolean iceRestart = false;
|
||||
|
||||
// Mozilla proprietary options (at risk: Bug 1196974)
|
||||
boolean mozDontOfferDataChannel;
|
||||
boolean mozBundleOnly;
|
||||
};
|
||||
|
||||
interface RTCDataChannel;
|
||||
|
||||
36
dom/xbl/crashtests/1382357.xhtml
Normal file
36
dom/xbl/crashtests/1382357.xhtml
Normal file
@@ -0,0 +1,36 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
|
||||
<head>
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl">
|
||||
<binding id="foo"><content><span xmlns="http://www.w3.org/1999/xhtml"/></content></binding>
|
||||
</bindings>
|
||||
|
||||
<script type="text/javascript">
|
||||
// <![CDATA[
|
||||
|
||||
function boom()
|
||||
{
|
||||
// Apply an invalid binding, and wait two ticks of the refresh driver for the async load.
|
||||
var d = document.getElementById("d");
|
||||
d.style.MozBinding = "url(#bar)";
|
||||
requestAnimationFrame(function() {
|
||||
requestAnimationFrame(function() {
|
||||
// Trigger the InnerText getter.
|
||||
document.getElementById("s").innerText;
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="boom();">
|
||||
|
||||
<div id="d">
|
||||
<span id="s">SpanText</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -38,3 +38,4 @@ load 507991-1.xhtml
|
||||
load 830614-1.xul
|
||||
load 895805-1.xhtml
|
||||
load set-field-bad-this.xhtml
|
||||
load 1382357.xhtml
|
||||
|
||||
@@ -116,7 +116,15 @@ public:
|
||||
if (!doc)
|
||||
return;
|
||||
|
||||
// Destroy the frames for mBoundElement.
|
||||
// Get the binding.
|
||||
bool ready = false;
|
||||
nsXBLService::GetInstance()->BindingReady(mBoundElement, mBindingURI, &ready);
|
||||
if (!ready)
|
||||
return;
|
||||
|
||||
// Destroy the frames for mBoundElement. Do this after getting the binding,
|
||||
// since if the binding fetch fails then we don't want to destroy the
|
||||
// frames.
|
||||
nsIContent* destroyedFramesFor = nullptr;
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
if (shell) {
|
||||
@@ -124,12 +132,6 @@ public:
|
||||
}
|
||||
MOZ_ASSERT(!mBoundElement->GetPrimaryFrame());
|
||||
|
||||
// Get the binding.
|
||||
bool ready = false;
|
||||
nsXBLService::GetInstance()->BindingReady(mBoundElement, mBindingURI, &ready);
|
||||
if (!ready)
|
||||
return;
|
||||
|
||||
// If |mBoundElement| is (in addition to having binding |mBinding|)
|
||||
// also a descendant of another element with binding |mBinding|,
|
||||
// then we might have just constructed it due to the
|
||||
|
||||
@@ -5319,7 +5319,7 @@ EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t minOffset = INT32_MAX;
|
||||
uint32_t minOffset = UINT32_MAX;
|
||||
static const SelectionType kIMESelectionTypes[] = {
|
||||
SelectionType::eIMERawClause,
|
||||
SelectionType::eIMESelectedRawClause,
|
||||
@@ -5339,15 +5339,11 @@ EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
|
||||
if (NS_WARN_IF(range->GetStartContainer() != aTextNode)) {
|
||||
// ignore the start offset...
|
||||
} else {
|
||||
MOZ_ASSERT(range->StartOffset() >= 0,
|
||||
"start offset shouldn't be negative");
|
||||
minOffset = std::min(minOffset, range->StartOffset());
|
||||
}
|
||||
if (NS_WARN_IF(range->GetEndContainer() != aTextNode)) {
|
||||
// ignore the end offset...
|
||||
} else {
|
||||
MOZ_ASSERT(range->EndOffset() >= 0,
|
||||
"start offset shouldn't be negative");
|
||||
minOffset = std::min(minOffset, range->EndOffset());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,7 +458,7 @@ HTMLEditRules::AfterEditInner(EditAction action,
|
||||
NS_ENSURE_STATE(selection);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> rangeStartContainer, rangeEndContainer;
|
||||
int32_t rangeStartOffset = 0, rangeEndOffset = 0;
|
||||
uint32_t rangeStartOffset = 0, rangeEndOffset = 0;
|
||||
// do we have a real range to act on?
|
||||
bool bDamagedRange = false;
|
||||
if (mDocChangeRange) {
|
||||
@@ -568,8 +568,8 @@ HTMLEditRules::AfterEditInner(EditAction action,
|
||||
action, selection,
|
||||
GetAsDOMNode(mRangeItem->mStartContainer),
|
||||
mRangeItem->mStartOffset,
|
||||
rangeStartContainer, rangeStartOffset,
|
||||
rangeEndContainer, rangeEndOffset);
|
||||
rangeStartContainer, static_cast<int32_t>(rangeStartOffset),
|
||||
rangeEndContainer, static_cast<int32_t>(rangeEndOffset));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// detect empty doc
|
||||
@@ -5317,9 +5317,8 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection)
|
||||
RefPtr<nsRange> range = inSelection->GetRangeAt(0);
|
||||
NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
||||
int32_t startOffset, endOffset;
|
||||
uint32_t startOffset, endOffset;
|
||||
nsCOMPtr<nsIDOMNode> newStartNode, newEndNode;
|
||||
int32_t newStartOffset, newEndOffset;
|
||||
|
||||
rv = range->GetStartContainer(getter_AddRefs(startNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@@ -5332,22 +5331,22 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection)
|
||||
|
||||
// adjusted values default to original values
|
||||
newStartNode = startNode;
|
||||
newStartOffset = startOffset;
|
||||
uint32_t newStartOffset = startOffset;
|
||||
newEndNode = endNode;
|
||||
newEndOffset = endOffset;
|
||||
uint32_t newEndOffset = endOffset;
|
||||
|
||||
// some locals we need for whitespace code
|
||||
nsCOMPtr<nsINode> unused;
|
||||
int32_t offset;
|
||||
int32_t offset = -1;
|
||||
WSType wsType;
|
||||
|
||||
// let the whitespace code do the heavy lifting
|
||||
WSRunObject wsEndObj(mHTMLEditor, endNode, endOffset);
|
||||
WSRunObject wsEndObj(mHTMLEditor, endNode, static_cast<int32_t>(endOffset));
|
||||
// is there any intervening visible whitespace? if so we can't push selection past that,
|
||||
// it would visibly change maening of users selection
|
||||
nsCOMPtr<nsINode> endNode_(do_QueryInterface(endNode));
|
||||
wsEndObj.PriorVisibleNode(endNode_, endOffset, address_of(unused),
|
||||
&offset, &wsType);
|
||||
wsEndObj.PriorVisibleNode(endNode_, static_cast<int32_t>(endOffset),
|
||||
address_of(unused), &offset, &wsType);
|
||||
if (wsType != WSType::text && wsType != WSType::normalWS) {
|
||||
// eThisBlock and eOtherBlock conveniently distinquish cases
|
||||
// of going "down" into a block and "up" out of a block.
|
||||
@@ -5357,36 +5356,44 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection)
|
||||
GetAsDOMNode(mHTMLEditor->GetRightmostChild(wsEndObj.mStartReasonNode,
|
||||
true));
|
||||
if (child) {
|
||||
newEndNode = EditorBase::GetNodeLocation(child, &newEndOffset);
|
||||
++newEndOffset; // offset *after* child
|
||||
int32_t offset = -1;
|
||||
newEndNode = EditorBase::GetNodeLocation(child, &offset);
|
||||
// offset *after* child
|
||||
newEndOffset = static_cast<uint32_t>(offset + 1);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsEndObj.mStartReason == WSType::thisBlock) {
|
||||
// endpoint is just after start of this block
|
||||
nsCOMPtr<nsIDOMNode> child;
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
mHTMLEditor->GetPriorHTMLNode(endNode, endOffset, address_of(child));
|
||||
mHTMLEditor->GetPriorHTMLNode(endNode, static_cast<int32_t>(endOffset),
|
||||
address_of(child));
|
||||
if (child) {
|
||||
newEndNode = EditorBase::GetNodeLocation(child, &newEndOffset);
|
||||
++newEndOffset; // offset *after* child
|
||||
int32_t offset = -1;
|
||||
newEndNode = EditorBase::GetNodeLocation(child, &offset);
|
||||
// offset *after* child
|
||||
newEndOffset = static_cast<uint32_t>(offset + 1);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsEndObj.mStartReason == WSType::br) {
|
||||
// endpoint is just after break. lets adjust it to before it.
|
||||
int32_t offset = -1;
|
||||
newEndNode =
|
||||
EditorBase::GetNodeLocation(GetAsDOMNode(wsEndObj.mStartReasonNode),
|
||||
&newEndOffset);
|
||||
&offset);
|
||||
newEndOffset = static_cast<uint32_t>(offset);;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// similar dealio for start of range
|
||||
WSRunObject wsStartObj(mHTMLEditor, startNode, startOffset);
|
||||
WSRunObject wsStartObj(mHTMLEditor, startNode,
|
||||
static_cast<int32_t>(startOffset));
|
||||
// is there any intervening visible whitespace? if so we can't push selection past that,
|
||||
// it would visibly change maening of users selection
|
||||
nsCOMPtr<nsINode> startNode_(do_QueryInterface(startNode));
|
||||
wsStartObj.NextVisibleNode(startNode_, startOffset, address_of(unused),
|
||||
&offset, &wsType);
|
||||
wsStartObj.NextVisibleNode(startNode_, static_cast<int32_t>(startOffset),
|
||||
address_of(unused), &offset, &wsType);
|
||||
if (wsType != WSType::text && wsType != WSType::normalWS) {
|
||||
// eThisBlock and eOtherBlock conveniently distinquish cases
|
||||
// of going "down" into a block and "up" out of a block.
|
||||
@@ -5396,24 +5403,31 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection)
|
||||
GetAsDOMNode(mHTMLEditor->GetLeftmostChild(wsStartObj.mEndReasonNode,
|
||||
true));
|
||||
if (child) {
|
||||
newStartNode = EditorBase::GetNodeLocation(child, &newStartOffset);
|
||||
int32_t offset = -1;
|
||||
newStartNode = EditorBase::GetNodeLocation(child, &offset);
|
||||
newStartOffset = static_cast<uint32_t>(offset);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsStartObj.mEndReason == WSType::thisBlock) {
|
||||
// startpoint is just before end of this block
|
||||
nsCOMPtr<nsIDOMNode> child;
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(child));
|
||||
mHTMLEditor->GetNextHTMLNode(startNode, static_cast<int32_t>(startOffset),
|
||||
address_of(child));
|
||||
if (child) {
|
||||
newStartNode = EditorBase::GetNodeLocation(child, &newStartOffset);
|
||||
int32_t offset = -1;
|
||||
newStartNode = EditorBase::GetNodeLocation(child, &offset);
|
||||
newStartOffset = static_cast<uint32_t>(offset);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsStartObj.mEndReason == WSType::br) {
|
||||
// startpoint is just before a break. lets adjust it to after it.
|
||||
int32_t offset = -1;
|
||||
newStartNode =
|
||||
EditorBase::GetNodeLocation(GetAsDOMNode(wsStartObj.mEndReasonNode),
|
||||
&newStartOffset);
|
||||
++newStartOffset; // offset *after* break
|
||||
&offset);
|
||||
// offset *after* break
|
||||
newStartOffset = static_cast<uint32_t>(offset + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8193,7 +8207,7 @@ HTMLEditRules::UpdateDocChangeRange(nsRange* aRange)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Positive result means mDocChangeRange start is after aRange start.
|
||||
if (result > 0) {
|
||||
int32_t startOffset;
|
||||
uint32_t startOffset;
|
||||
rv = aRange->GetStartOffset(&startOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mDocChangeRange->SetStart(startNode, startOffset);
|
||||
@@ -8207,9 +8221,9 @@ HTMLEditRules::UpdateDocChangeRange(nsRange* aRange)
|
||||
// Negative result means mDocChangeRange end is before aRange end.
|
||||
if (result < 0) {
|
||||
nsCOMPtr<nsIDOMNode> endNode;
|
||||
int32_t endOffset;
|
||||
rv = aRange->GetEndContainer(getter_AddRefs(endNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
uint32_t endOffset;
|
||||
rv = aRange->GetEndOffset(&endOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mDocChangeRange->SetEnd(endNode, endOffset);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user