merge autoland to mozilla-central. r=merge a=merge

MozReview-Commit-ID: D8YSuNsBw9o
This commit is contained in:
Sebastian Hengst 2017-10-19 00:04:37 +02:00
Родитель d0448c9700 62ec8eba92
Коммит 3e8ed7e2b5
311 изменённых файлов: 84738 добавлений и 43547 удалений

Просмотреть файл

@ -30,7 +30,6 @@ tools/update-packaging/**
uriloader/**
view/**
widget/**
xpcom/**
# We currently have no js files in these directories, so we ignore them by
# default to aid ESLint's performance.

Просмотреть файл

@ -16,8 +16,7 @@ function setE10sPrefs() {
SpecialPowers.pushPrefEnv({
set: [
["browser.tabs.remote.autostart", true],
["browser.tabs.remote.force-enable", true],
["extensions.e10sBlocksEnabling", false]
["browser.tabs.remote.force-enable", true]
]
}, resolve));
}

Просмотреть файл

@ -107,4 +107,4 @@ endif
.PHONY: features
tools features::
$(PYTHON) -c 'import os, json; listing = {"system": os.listdir("$(DIST)/bin/browser/features")}; print json.dumps(listing)' > $(DIST)/bin/browser/chrome/browser/content/browser/built_in_addons.json
$(PYTHON) -c 'import os, json; listing = {"system": sorted(os.listdir("$(DIST)/bin/browser/features"))}; print json.dumps(listing)' > $(DIST)/bin/browser/chrome/browser/content/browser/built_in_addons.json

Просмотреть файл

@ -620,25 +620,32 @@ pref("browser.snapshots.limit", 0);
// 1: Scrolling contents
// 2: Go back or go forward, in your history
// 3: Zoom in or out.
// 4: Treat vertical wheel as horizontal scroll
#ifdef XP_MACOSX
// On OS X, if the wheel has one axis only, shift+wheel comes through as a
// On macOS, if the wheel has one axis only, shift+wheel comes through as a
// horizontal scroll event. Thus, we can't assign anything other than normal
// scrolling to shift+wheel.
pref("mousewheel.with_alt.action", 2);
pref("mousewheel.with_shift.action", 1);
pref("mousewheel.with_alt.action", 2);
// On MacOS X, control+wheel is typically handled by system and we don't
// receive the event. So, command key which is the main modifier key for
// acceleration is the best modifier for zoom-in/out. However, we should keep
// the control key setting for backward compatibility.
pref("mousewheel.with_meta.action", 3); // command key on Mac
// Disable control-/meta-modified horizontal mousewheel events, since
// those are used on Mac as part of modified swipe gestures (e.g.
// Left swipe+Cmd = go back in a new tab).
// Disable control-/meta-modified horizontal wheel events, since those are
// used on Mac as part of modified swipe gestures (e.g. Left swipe+Cmd is
// "go back" in a new tab).
pref("mousewheel.with_control.action.override_x", 0);
pref("mousewheel.with_meta.action.override_x", 0);
#else
pref("mousewheel.with_alt.action", 1);
pref("mousewheel.with_shift.action", 2);
// On the other platforms (non-macOS), user may use legacy mouse which supports
// only vertical wheel but want to scroll horizontally. For such users, we
// should provide horizontal scroll with shift+wheel (same as Chrome).
// However, shift+wheel was used for navigating history. For users who want
// to keep using this feature, let's enable it with alt+wheel. This is better
// for consistency with macOS users.
pref("mousewheel.with_shift.action", 4);
pref("mousewheel.with_alt.action", 2);
pref("mousewheel.with_meta.action", 1); // win key on Win, Super/Hyper on Linux
#endif
pref("mousewheel.with_control.action",3);
@ -1552,17 +1559,9 @@ pref("privacy.userContext.longPressBehavior", 0);
pref("privacy.userContext.extension", "");
// Start the browser in e10s mode
pref("browser.tabs.remote.autostart", false);
pref("browser.tabs.remote.autostart", true);
pref("browser.tabs.remote.desktopbehavior", true);
#if !defined(RELEASE_OR_BETA) || defined(MOZ_DEV_EDITION)
// At the moment, autostart.2 is used, while autostart.1 is unused.
// We leave it here set to false to reset users' defaults and allow
// us to change everybody to true in the future, when desired.
pref("browser.tabs.remote.autostart.1", false);
pref("browser.tabs.remote.autostart.2", true);
#endif
// For speculatively warming up tabs to improve perceived
// performance while using the async tab switcher.
// Disabled until bug 1397426 is fixed.
@ -1588,12 +1587,6 @@ pref("extensions.allow-non-mpc-extensions", false);
pref("extensions.legacy.enabled", false);
// Enable blocking of e10s and e10s-multi for add-on users on beta/release.
#if defined(RELEASE_OR_BETA) && !defined(MOZ_DEV_EDITION)
pref("extensions.e10sBlocksEnabling", true);
pref("extensions.e10sMultiBlocksEnabling", true);
#endif
// How often to check for CPOW timeouts. CPOWs are only timed out by
// the hang monitor.
pref("dom.ipc.cpow.timeout", 500);

Просмотреть файл

@ -67,7 +67,6 @@ var SidebarUI = {
// If this is the last browser window, persist various values that should be
// remembered for after a restart / reopening a browser window.
let enumerator = Services.wm.getEnumerator("navigator:browser");
enumerator.getNext();
if (!enumerator.hasMoreElements()) {
document.persist("sidebar-box", "sidebarcommand");

Просмотреть файл

@ -27,6 +27,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
PageMetadata: "resource://gre/modules/PageMetadata.jsm",
PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
Utils: "resource://gre/modules/sessionstore/Utils.jsm",
WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
Feeds: "resource:///modules/Feeds.jsm",
@ -156,14 +157,8 @@ var AboutBlockedSiteListener = {
return;
}
let provider = "";
if (docShell.failedChannel) {
let classifiedChannel = docShell.failedChannel.
QueryInterface(Ci.nsIClassifiedChannel);
if (classifiedChannel) {
provider = classifiedChannel.matchedProvider;
}
}
let blockedInfo = getSiteBlockedErrorDetails(docShell);
let provider = blockedInfo.provider || "";
let doc = content.document;
@ -182,7 +177,8 @@ var AboutBlockedSiteListener = {
switch (aEvent.detail.err) {
case "malware":
doc.getElementById("report_detection").setAttribute("href",
"https://www.stopbadware.org/firefox");
(SafeBrowsing.getReportURL("MalwareMistake", blockedInfo) ||
"https://www.stopbadware.org/firefox"));
doc.getElementById("learn_more_link").setAttribute("href",
"https://www.stopbadware.org/firefox");
break;
@ -192,7 +188,8 @@ var AboutBlockedSiteListener = {
break;
case "phishing":
doc.getElementById("report_detection").setAttribute("href",
"https://safebrowsing.google.com/safebrowsing/report_error/?tpl=mozilla");
(SafeBrowsing.getReportURL("PhishMistake", blockedInfo) ||
"https://safebrowsing.google.com/safebrowsing/report_error/?tpl=mozilla"));
doc.getElementById("learn_more_link").setAttribute("href",
"https://www.antiphishing.org//");
break;

Просмотреть файл

@ -13,7 +13,23 @@ function failIfSidebarFocusedFires() {
ok(false, "This event shouldn't have fired");
}
add_task(async function() {
add_task(async function testAdoptedTwoWindows() {
// First open a new window, show the sidebar in that window, and close it.
// Then, open another new window and confirm that the sidebar is closed since it is
// being adopted from the main window which doesn't have a shown sidebar. See Bug 1407737.
info("Ensure that sidebar state is adopted only from the opener");
let win1 = await BrowserTestUtils.openNewBrowserWindow({opener: window});
await win1.SidebarUI.show("viewBookmarksSidebar");
await BrowserTestUtils.closeWindow(win1);
let win2 = await BrowserTestUtils.openNewBrowserWindow({opener: window});
ok(!win2.document.getElementById("sidebar-button").hasAttribute("checked"), "Sidebar button isn't checked");
ok(!win2.SidebarUI.isOpen, "Sidebar is closed");
await BrowserTestUtils.closeWindow(win2);
});
add_task(async function testEventsReceivedInMainWindow() {
info("Opening the sidebar and expecting both SidebarShown and SidebarFocused events");
let initialShown = BrowserTestUtils.waitForEvent(window, "SidebarShown");
@ -26,7 +42,7 @@ add_task(async function() {
ok(true, "SidebarShown and SidebarFocused events fired on a new window");
});
add_task(async function() {
add_task(async function testEventReceivedInNewWindow() {
info("Opening a new window and expecting the SidebarFocused event to not fire");
let promiseNewWindow = BrowserTestUtils.waitForNewWindow(false);

Просмотреть файл

@ -29,9 +29,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
<xul:hbox anonid="textbox-container"
class="autocomplete-textbox-container urlbar-textbox-container"
flex="1" xbl:inherits="focused">
<children includes="image|deck|stack|box">
<xul:image class="autocomplete-icon" allowevents="true"/>
</children>
<children includes="image|deck|stack|box"/>
<xul:hbox anonid="textbox-input-box"
class="textbox-input-box urlbar-input-box"
flex="1" xbl:inherits="tooltiptext=inputtooltiptext">

Просмотреть файл

@ -346,6 +346,10 @@ this.tabs = class extends ExtensionAPI {
if (!context.checkLoadURL(url, {dontReportErrors: true})) {
return Promise.reject({message: `Illegal URL: ${url}`});
}
if (createProperties.openInReaderMode) {
url = `about:reader?url=${encodeURIComponent(url)}`;
}
}
if (createProperties.cookieStoreId && !extension.hasPermission("cookies")) {

Просмотреть файл

@ -80,7 +80,14 @@ this.windows = class extends ExtensionAPI {
},
getAll: function(getInfo) {
let windows = Array.from(windowManager.getAll(), win => win.convert(getInfo));
let doNotCheckTypes = getInfo === null || getInfo.windowTypes === null;
function typeFilter(win) {
return doNotCheckTypes || getInfo.windowTypes.includes(win.type);
}
let windows = Array.from(windowManager.getAll(), win => win.convert(getInfo))
.filter(typeFilter);
return Promise.resolve(windows);
},

Просмотреть файл

@ -497,7 +497,10 @@
"type": "string",
"optional": true,
"description": "The CookieStoreId for the tab that opened this tab."
}
},
"openInReaderMode": {
"type": "boolean",
"optional": true, "description": "Whether the document in the tab should be opened in reader mode."}
}
},
{

Просмотреть файл

@ -32,6 +32,7 @@ support-files =
searchSuggestionEngine.xml
searchSuggestionEngine.sjs
../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js
../../../../../toolkit/components/extensions/test/mochitest/redirection.sjs
../../../../../toolkit/components/reader/test/readerModeNonArticle.html
../../../../../toolkit/components/reader/test/readerModeArticle.html
@ -84,6 +85,7 @@ skip-if = (os == 'win' && !debug) # bug 1352668
[browser_ext_find.js]
[browser_ext_geckoProfiler_symbolicate.js]
[browser_ext_getViews.js]
[browser_ext_history_redirect.js]
[browser_ext_identity_indication.js]
[browser_ext_incognito_views.js]
[browser_ext_incognito_popup.js]

Просмотреть файл

@ -5,7 +5,7 @@
function frameScript() {
function getSelectedText() {
let frame = this.content.frames[0].frames[1];
let Ci = Components.interfaces;
let {interfaces: Ci, utils: Cu} = Components;
let docShell = frame.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
@ -14,10 +14,18 @@ function frameScript() {
.QueryInterface(Ci.nsISelectionController);
let selection = controller.getSelection(controller.SELECTION_FIND);
let range = selection.getRangeAt(0);
let scope = {};
Cu.import("resource://gre/modules/FindContent.jsm", scope);
let highlighter = (new scope.FindContent(docShell)).highlighter;
let r1 = frame.parent.frameElement.getBoundingClientRect();
let f1 = highlighter._getFrameElementOffsets(frame.parent);
let r2 = frame.frameElement.getBoundingClientRect();
let f2 = highlighter._getFrameElementOffsets(frame);
let r3 = range.getBoundingClientRect();
let rect = {top: (r1.top + r2.top + r3.top), left: (r1.left + r2.left + r3.left)};
let rect = {
top: (r1.top + r2.top + r3.top + f1.y + f2.y),
left: (r1.left + r2.left + r3.left + f1.x + f2.x),
};
this.sendAsyncMessage("test:find:selectionTest", {text: selection.toString(), rect});
}
getSelectedText();

Просмотреть файл

@ -1,26 +1,8 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for simple WebExtension</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
const REDIRECT_URL = new URL("redirection.sjs", location).href;
function waitForLoad(win) {
return new Promise(resolve => {
win.addEventListener("load", function() {
resolve();
}, {capture: true, once: true});
});
}
const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
const REDIRECT_URL = BASE + "/redirection.sjs";
const REDIRECTED_URL = BASE + "/dummy_page.html";
add_task(async function history_redirect() {
function background() {
@ -64,24 +46,18 @@ add_task(async function history_redirect() {
extension.sendMessage("delete-all");
await extension.awaitMessage("delete-all-result");
let win = window.open(REDIRECT_URL);
await waitForLoad(win);
await BrowserTestUtils.withNewTab({gBrowser, url: REDIRECT_URL}, async (browser) => {
is(browser.currentURI.spec, REDIRECTED_URL, "redirected to the expected location");
extension.sendMessage("search", REDIRECT_URL);
let results = await extension.awaitMessage("search-result");
is(results.length, 1, "search returned expected length of results");
extension.sendMessage("search", REDIRECT_URL);
let results = await extension.awaitMessage("search-result");
is(results.length, 1, "search returned expected length of results");
extension.sendMessage("get-visits", REDIRECT_URL);
let visits = await extension.awaitMessage("get-visits-result");
is(visits.length, 1, "getVisits returned expected length of visits");
// cleanup phase
win.close();
extension.sendMessage("get-visits", REDIRECT_URL);
let visits = await extension.awaitMessage("get-visits-result");
is(visits.length, 1, "getVisits returned expected length of visits");
});
await extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

Просмотреть файл

@ -59,6 +59,10 @@ add_task(async function test_create_options() {
create: {url: "blank.html"},
result: {url: browser.runtime.getURL("bg/blank.html")},
},
{
create: {url: "http://example.com/", openInReaderMode: true},
result: {url: `about:reader?url=${encodeURIComponent("http://example.com/")}`},
},
{
create: {},
result: {url: "about:newtab"},

Просмотреть файл

@ -11,22 +11,43 @@ add_task(async function testWindowGetAll() {
subject => subject == raisedWin);
let extension = ExtensionTestUtils.loadExtension({
background: function() {
browser.windows.getAll((wins) => {
browser.test.assertEq(wins.length, 2, "Expect two windows");
background: async function() {
let wins = await browser.windows.getAll();
browser.test.assertEq(2, wins.length, "Expect two windows");
browser.test.assertEq(false, wins[0].alwaysOnTop,
"Expect first window not to be always on top");
browser.test.assertEq(true, wins[1].alwaysOnTop,
"Expect first window to be always on top");
browser.test.assertEq(false, wins[0].alwaysOnTop,
"Expect first window not to be always on top");
browser.test.assertEq(true, wins[1].alwaysOnTop,
"Expect first window to be always on top");
browser.test.notifyPass("alwaysOnTop");
});
let win = await browser.windows.create({url: "http://example.com", type: "popup"});
wins = await browser.windows.getAll();
browser.test.assertEq(3, wins.length, "Expect three windows");
wins = await browser.windows.getAll({windowTypes: ["popup"]});
browser.test.assertEq(1, wins.length, "Expect one window");
browser.test.assertEq("popup", wins[0].type,
"Expect type to be popup");
wins = await browser.windows.getAll({windowTypes: ["normal"]});
browser.test.assertEq(2, wins.length, "Expect two windows");
browser.test.assertEq("normal", wins[0].type,
"Expect type to be normal");
browser.test.assertEq("normal", wins[1].type,
"Expect type to be normal");
wins = await browser.windows.getAll({windowTypes: ["popup", "normal"]});
browser.test.assertEq(3, wins.length, "Expect three windows");
await browser.windows.remove(win.id);
browser.test.notifyPass("getAll");
},
});
await extension.startup();
await extension.awaitFinish("alwaysOnTop");
await extension.awaitFinish("getAll");
await extension.unload();
await BrowserTestUtils.closeWindow(raisedWin);

Просмотреть файл

@ -2,8 +2,6 @@
support-files =
../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
../../../../../toolkit/components/extensions/test/mochitest/file_sample.html
../../../../../toolkit/components/extensions/test/mochitest/redirection.sjs
tags = webextensions
[test_ext_all_apis.html]
[test_ext_history_redirect.html]

Просмотреть файл

@ -1193,20 +1193,11 @@ var gMainPane = {
let processCountPref = document.getElementById("dom.ipc.processCount");
if (defaultPerformancePref.value) {
let accelerationPref = document.getElementById("layers.acceleration.disabled");
// Unset the value so process count will be decided by e10s rollout.
// Unset the value so process count will be decided by the platform.
processCountPref.value = processCountPref.defaultValue;
accelerationPref.value = accelerationPref.defaultValue;
performanceSettings.hidden = true;
} else {
let e10sRolloutProcessCountPref =
document.getElementById("dom.ipc.processCount.web");
// Take the e10s rollout value as the default value (if it exists),
// but don't overwrite the user set value.
if (duringChangeEvent &&
e10sRolloutProcessCountPref.value &&
processCountPref.value == processCountPref.defaultValue) {
processCountPref.value = e10sRolloutProcessCountPref.value;
}
performanceSettings.hidden = false;
}
},
@ -1214,10 +1205,7 @@ var gMainPane = {
buildContentProcessCountMenuList() {
if (Services.appinfo.browserTabsRemoteAutostart) {
let processCountPref = document.getElementById("dom.ipc.processCount");
let e10sRolloutProcessCountPref =
document.getElementById("dom.ipc.processCount.web");
let defaultProcessCount =
e10sRolloutProcessCountPref.value || processCountPref.defaultValue;
let defaultProcessCount = processCountPref.defaultValue;
let bundlePreferences = document.getElementById("bundlePreferences");
let label = bundlePreferences.getFormattedString("defaultContentProcessCount",
[defaultProcessCount]);

Просмотреть файл

@ -176,10 +176,19 @@ var gPrivacyPane = {
setEventListener("notificationsDoNotDisturb", "command",
gPrivacyPane.toggleDoNotDisturbNotifications);
let bundlePrefs = document.getElementById("bundlePreferences");
if (AlertsServiceDND) {
let notificationsDoNotDisturbBox =
document.getElementById("notificationsDoNotDisturbBox");
notificationsDoNotDisturbBox.removeAttribute("hidden");
let checkbox = document.getElementById("notificationsDoNotDisturb");
let brandName = document.getElementById("bundleBrand")
.getString("brandShortName");
checkbox.setAttribute("label",
bundlePrefs.getFormattedString("pauseNotifications.label",
[brandName]));
checkbox.setAttribute("accesskey", bundlePrefs.getString("pauseNotifications.accesskey"));
if (AlertsServiceDND.manualDoNotDisturb) {
let notificationsDoNotDisturb =
document.getElementById("notificationsDoNotDisturb");
@ -201,7 +210,6 @@ var gPrivacyPane = {
gPrivacyPane.removeOfflineApp);
setEventListener("clearOfflineAppCacheButton", "command",
gPrivacyPane.clearOfflineAppCache);
let bundlePrefs = document.getElementById("bundlePreferences");
document.getElementById("offlineAppsList")
.style.height = bundlePrefs.getString("offlineAppsList.height");
let offlineGroup = document.getElementById("offlineGroup");
@ -254,7 +262,6 @@ var gPrivacyPane = {
setEventListener("submitHealthReportBox", "command",
gPrivacyPane.updateSubmitHealthReport);
this._initA11yState();
let bundlePrefs = document.getElementById("bundlePreferences");
let signonBundle = document.getElementById("signonBundle");
let pkiBundle = document.getElementById("pkiBundle");
appendSearchKeywords("passwordExceptions", [

Просмотреть файл

@ -651,15 +651,12 @@
</rows>
</grid>
<separator flex="1"/>
<vbox id="notificationsDoNotDisturbBox" hidden="true">
<checkbox id="notificationsDoNotDisturb" label="&notificationsDoNotDisturb.label;"
accesskey="&notificationsDoNotDisturb.accesskey;"/>
<description id="notificationsDoNotDisturbDetails"
class="indent tip-caption">&notificationsDoNotDisturbDetails.value;</description>
<checkbox id="notificationsDoNotDisturb" class="indent"/>
</vbox>
<separator flex="1"/>
<hbox align="start">
<checkbox id="popupPolicy" preference="dom.disable_open_during_load"
label="&blockPopups.label;" accesskey="&blockPopups.accesskey;"

Просмотреть файл

@ -9,9 +9,8 @@ add_task(async function() {
]});
});
add_task(async function() {
Services.prefs.clearUserPref("dom.ipc.processCount");
Services.prefs.setIntPref("dom.ipc.processCount.web", DEFAULT_PROCESS_COUNT + 1);
add_task(async function testPrefsAreDefault() {
Services.prefs.setIntPref("dom.ipc.processCount", DEFAULT_PROCESS_COUNT);
Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
@ -21,7 +20,7 @@ add_task(async function() {
let useRecommendedPerformanceSettings = doc.querySelector("#useRecommendedPerformanceSettings");
is(Services.prefs.getBoolPref("browser.preferences.defaultPerformanceSettings.enabled"), true,
"pref value should be true before clicking on checkbox");
"pref value should be true before clicking on checkbox");
ok(useRecommendedPerformanceSettings.checked, "checkbox should be checked before clicking on checkbox");
useRecommendedPerformanceSettings.click();
@ -35,43 +34,17 @@ add_task(async function() {
let contentProcessCount = doc.querySelector("#contentProcessCount");
is(contentProcessCount.disabled, false, "process count control should be enabled");
is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT + 1, "e10s rollout value should be default value");
is(contentProcessCount.selectedItem.value, DEFAULT_PROCESS_COUNT + 1, "selected item should be the default one");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.prefs.clearUserPref("dom.ipc.processCount");
Services.prefs.clearUserPref("dom.ipc.processCount.web");
Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
});
add_task(async function() {
Services.prefs.clearUserPref("dom.ipc.processCount");
Services.prefs.setIntPref("dom.ipc.processCount.web", DEFAULT_PROCESS_COUNT + 1);
Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", false);
let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
is(prefs.selectedPane, "paneGeneral", "General pane was selected");
let doc = gBrowser.contentDocument;
let performanceSettings = doc.querySelector("#performanceSettings");
is(performanceSettings.hidden, false, "performance settings section is shown");
let contentProcessCount = doc.querySelector("#contentProcessCount");
is(contentProcessCount.disabled, false, "process count control should be enabled");
is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT, "default value should be the current value");
is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT, "default pref should be default value");
is(contentProcessCount.selectedItem.value, DEFAULT_PROCESS_COUNT, "selected item should be the default one");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.prefs.clearUserPref("dom.ipc.processCount");
Services.prefs.clearUserPref("dom.ipc.processCount.web");
Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
});
add_task(async function() {
add_task(async function testPrefsSetByUser() {
Services.prefs.setIntPref("dom.ipc.processCount", DEFAULT_PROCESS_COUNT + 2);
Services.prefs.setIntPref("dom.ipc.processCount.web", DEFAULT_PROCESS_COUNT + 1);
Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", false);
let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
@ -97,6 +70,5 @@ add_task(async function() {
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.prefs.clearUserPref("dom.ipc.processCount");
Services.prefs.clearUserPref("dom.ipc.processCount.web");
Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
});

Просмотреть файл

@ -16,11 +16,13 @@ support-files =
[browser_roundedWindow_newWindow.js]
[browser_roundedWindow_open_max_inner.js]
[browser_roundedWindow_open_max_outer.js]
[browser_roundedWindow_open_mid.js]
[browser_roundedWindow_open_mid_inner.js]
[browser_roundedWindow_open_mid_outer.js]
[browser_roundedWindow_open_min.js]
[browser_roundedWindow_windowSetting_max_inner.js]
[browser_roundedWindow_windowSetting_max_outer.js]
[browser_roundedWindow_windowSetting_mid.js]
[browser_roundedWindow_windowSetting_mid_inner.js]
[browser_roundedWindow_windowSetting_mid_outer.js]
[browser_roundedWindow_windowSetting_min_inner.js]
[browser_roundedWindow_windowSetting_min_outer.js]
[browser_timezone.js]

Просмотреть файл

@ -0,0 +1,11 @@
/*
* Bug 1330882 - A test case for opening new windows through window.open() as
* rounded size when fingerprinting resistance is enabled. This test is for
* middle values.
*/
OpenTest.run([
{settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600},
{settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600},
{settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600}
], false);

Просмотреть файл

@ -8,4 +8,4 @@ OpenTest.run([
{settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600},
{settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600},
{settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600}
]);
], true);

Просмотреть файл

@ -1,11 +1,10 @@
/*
* Bug 1330882 - A test case for setting window size through window.innerWidth/Height
* and window.outerWidth/Height when fingerprinting resistance is enabled. This
* test is for middle values.
* when fingerprinting resistance is enabled. This test is for middle values.
*/
WindowSettingTest.run([
{settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100},
{settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100},
{settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100}
]);
], false);

Просмотреть файл

@ -0,0 +1,10 @@
/*
* Bug 1330882 - A test case for setting window size through window.outerWidth/Height
* when fingerprinting resistance is enabled. This test is for middle values.
*/
WindowSettingTest.run([
{settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100},
{settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100},
{settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100}
], true);

306
browser/extensions/e10srollout/bootstrap.js поставляемый
Просмотреть файл

@ -1,306 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/UpdateUtils.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/TelemetryEnvironment.jsm");
// The amount of people to be part of e10s
const TEST_THRESHOLD = {
"beta": 0.9, // 90%
"release": 1.0, // 100%
"esr": 1.0, // 100%
};
// If a user qualifies for the e10s-multi experiement, this is how many
// content processes to use and whether to allow addons for the experiment.
const MULTI_EXPERIMENT = {
"beta": { buckets: { 4: 1 }, // 4 processes: 100%
// The extensions code only allows webextensions and legacy-style
// extensions that have been verified to work with multi.
// Therefore, we can allow all extensions.
addonsDisableExperiment(prefix) { return false; } },
"release": { buckets: { 4: 1 }, // 4 processes: 100%
// See the comment above the "beta" addonsDisableExperiment.
addonsDisableExperiment(prefix) { return false; } }
};
const ADDON_ROLLOUT_POLICY = {
"beta": "50allmpc",
"release": "50allmpc",
"esr": "esrA", // WebExtensions and Addons with mpc=true
};
if (AppConstants.RELEASE_OR_BETA) {
// Bug 1348576 - e10s is never enabled for non-official release builds
// This is hacky, but the problem it solves is the following:
// the e10s rollout is controlled by the channel name, which
// is the only way to distinguish between Beta and Release.
// However, non-official release builds (like the ones done by distros
// to ship Firefox on their package managers) do not set a value
// for the release channel, which gets them to the default value
// of.. (drumroll) "default".
// But we can't just always configure the same settings for the
// "default" channel because that's also the name that a locally
// built Firefox gets, and e10s is managed in a different way
// there (directly by prefs, on Nightly and Aurora).
TEST_THRESHOLD.default = TEST_THRESHOLD.release;
ADDON_ROLLOUT_POLICY.default = ADDON_ROLLOUT_POLICY.release;
}
const PREF_COHORT_SAMPLE = "e10s.rollout.cohortSample";
const PREF_COHORT_NAME = "e10s.rollout.cohort";
const PREF_E10S_OPTED_IN = "browser.tabs.remote.autostart";
const PREF_E10S_FORCE_ENABLED = "browser.tabs.remote.force-enable";
const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
const PREF_TOGGLE_E10S = "browser.tabs.remote.autostart.2";
const PREF_E10S_ADDON_POLICY = "extensions.e10s.rollout.policy";
const PREF_E10S_ADDON_BLOCKLIST = "extensions.e10s.rollout.blocklist";
const PREF_E10S_HAS_NONEXEMPT_ADDON = "extensions.e10s.rollout.hasAddon";
const PREF_E10S_MULTI_OPTOUT = "dom.ipc.multiOptOut";
const PREF_E10S_PROCESSCOUNT = "dom.ipc.processCount";
const PREF_USE_DEFAULT_PERF_SETTINGS = "browser.preferences.defaultPerformanceSettings.enabled";
const PREF_E10S_MULTI_ADDON_BLOCKS = "extensions.e10sMultiBlocksEnabling";
const PREF_E10S_MULTI_BLOCKED_BY_ADDONS = "extensions.e10sMultiBlockedByAddons";
function startup() {
// In theory we only need to run this once (on install()), but
// it's better to also run it on every startup. If the user has
// made manual changes to the prefs, this will keep the data
// reported more accurate.
// It's also fine (and preferred) to just do it here on startup
// (instead of observing prefs), because e10s takes a restart
// to take effect, so we keep the data based on how it was when
// the session started.
defineCohort();
}
function install() {
defineCohort();
}
let cohortDefinedOnThisSession = false;
function defineCohort() {
// Avoid running twice when it was called by install() first
if (cohortDefinedOnThisSession) {
return;
}
cohortDefinedOnThisSession = true;
let updateChannel = UpdateUtils.getUpdateChannel(false);
if (!(updateChannel in TEST_THRESHOLD)) {
setCohort("unsupportedChannel");
return;
}
let addonPolicy = "unknown";
if (updateChannel in ADDON_ROLLOUT_POLICY) {
addonPolicy = ADDON_ROLLOUT_POLICY[updateChannel];
Services.prefs.setStringPref(PREF_E10S_ADDON_POLICY, addonPolicy);
// This is also the proper place to set the blocklist pref
// in case it is necessary.
Services.prefs.setStringPref(PREF_E10S_ADDON_BLOCKLIST, "");
} else {
Services.prefs.clearUserPref(PREF_E10S_ADDON_POLICY);
}
let userOptedOut = optedOut();
let userOptedIn = optedIn();
let disqualified = (Services.appinfo.multiprocessBlockPolicy != 0);
let testThreshold = TEST_THRESHOLD[updateChannel];
let testGroup = (getUserSample(false) < testThreshold);
let hasNonExemptAddon = Services.prefs.getBoolPref(PREF_E10S_HAS_NONEXEMPT_ADDON, false);
let temporaryDisqualification = getTemporaryDisqualification();
let temporaryQualification = getTemporaryQualification();
let cohortPrefix = "";
if (disqualified) {
cohortPrefix = "disqualified-";
} else if (hasNonExemptAddon) {
cohortPrefix = `addons-set${addonPolicy}-`;
}
let eligibleForMulti = false;
if (userOptedOut.e10s || userOptedOut.multi) {
// If we detected that the user opted out either for multi or e10s, then
// the proper prefs must already be set.
setCohort("optedOut");
} else if (userOptedIn.e10s) {
eligibleForMulti = true;
setCohort("optedIn");
} else if (temporaryDisqualification != "") {
// Users who are disqualified by the backend (from multiprocessBlockPolicy)
// can be put into either the test or control groups, because e10s will
// still be denied by the backend, which is useful so that the E10S_STATUS
// telemetry probe can be correctly set.
// For these volatile disqualification reasons, however, we must not try
// to activate e10s because the backend doesn't know about it. E10S_STATUS
// here will be accumulated as "2 - Disabled", which is fine too.
Services.prefs.clearUserPref(PREF_TOGGLE_E10S);
Services.prefs.clearUserPref(PREF_E10S_PROCESSCOUNT + ".web");
setCohort(`temp-disqualified-${temporaryDisqualification}`);
} else if (!disqualified && testThreshold < 1.0 &&
temporaryQualification != "") {
// Users who are qualified for e10s and on channels where some population
// would not receive e10s can be pushed into e10s anyway via a temporary
// qualification which overrides the user sample value when non-empty.
Services.prefs.setBoolPref(PREF_TOGGLE_E10S, true);
eligibleForMulti = true;
setCohort(`temp-qualified-${temporaryQualification}`);
} else if (testGroup) {
Services.prefs.setBoolPref(PREF_TOGGLE_E10S, true);
eligibleForMulti = true;
setCohort(`${cohortPrefix}test`);
} else {
Services.prefs.clearUserPref(PREF_TOGGLE_E10S);
Services.prefs.clearUserPref(PREF_E10S_PROCESSCOUNT + ".web");
setCohort(`${cohortPrefix}control`);
}
// Now determine if this user should be in the e10s-multi experiment.
// - We only run the experiment on channels defined in MULTI_EXPERIMENT.
// - If this experiment doesn't allow addons and we have a cohort prefix
// (i.e. there's at least one addon installed) we stop here.
// - We decided above whether this user qualifies for the experiment.
// - If the user already opted into multi, then their prefs are already set
// correctly, we're done.
// - If the user has addons that disqualify them for multi, leave them with
// the default number of content processes (1 on beta) but still in the
// test cohort.
if (!(updateChannel in MULTI_EXPERIMENT) ||
MULTI_EXPERIMENT[updateChannel].addonsDisableExperiment(cohortPrefix) ||
!eligibleForMulti ||
userOptedIn.multi ||
disqualified) {
Services.prefs.clearUserPref(PREF_E10S_PROCESSCOUNT + ".web");
return;
}
// If we got here with a cohortPrefix, it must be "addons-set50allmpc-",
// which means that there's at least one add-on installed. If
// getAddonsDisqualifyForMulti returns false, that means that all installed
// addons are webextension based, so note that in the cohort name.
if (cohortPrefix && !getAddonsDisqualifyForMulti()) {
cohortPrefix = "webextensions-";
}
// The user is in the multi experiment!
// Decide how many content processes to use for this user.
let buckets = MULTI_EXPERIMENT[updateChannel].buckets;
let multiUserSample = getUserSample(true);
for (let sampleName of Object.getOwnPropertyNames(buckets)) {
if (multiUserSample < buckets[sampleName]) {
// NB: Coerce sampleName to an integer because this is an integer pref.
Services.prefs.setIntPref(PREF_E10S_PROCESSCOUNT + ".web", +sampleName);
setCohort(`${cohortPrefix}multiBucket${sampleName}`);
break;
}
}
}
function shutdown(data, reason) {
}
function uninstall() {
}
function getUserSample(multi) {
let pref = multi ? (PREF_COHORT_SAMPLE + ".multi") : PREF_COHORT_SAMPLE;
let prefType = Services.prefs.getPrefType(pref);
if (prefType == Ci.nsIPrefBranch.PREF_STRING) {
return parseFloat(Services.prefs.getStringPref(pref), 10);
}
let value = 0.0;
if (prefType == Ci.nsIPrefBranch.PREF_INT) {
// convert old integer value
value = Services.prefs.getIntPref(pref) / 100;
} else {
value = Math.random();
}
Services.prefs.setStringPref(pref, value.toString().substr(0, 8));
return value;
}
function setCohort(cohortName) {
Services.prefs.setStringPref(PREF_COHORT_NAME, cohortName);
if (cohortName != "unsupportedChannel") {
TelemetryEnvironment.setExperimentActive("e10sCohort", cohortName);
}
try {
if (Ci.nsICrashReporter) {
Services.appinfo.QueryInterface(Ci.nsICrashReporter).annotateCrashReport("E10SCohort", cohortName);
}
} catch (e) {}
}
function optedIn() {
let e10s = Services.prefs.getBoolPref(PREF_E10S_OPTED_IN, false) ||
Services.prefs.getBoolPref(PREF_E10S_FORCE_ENABLED, false);
let multi = Services.prefs.prefHasUserValue(PREF_E10S_PROCESSCOUNT) ||
!Services.prefs.getBoolPref(PREF_USE_DEFAULT_PERF_SETTINGS, true);
return { e10s, multi };
}
function optedOut() {
// Users can also opt-out by toggling back the pref to false.
// If they reset the pref instead they might be re-enabled if
// they are still part of the threshold.
let e10s = Services.prefs.getBoolPref(PREF_E10S_FORCE_DISABLED, false) ||
(Services.prefs.prefHasUserValue(PREF_TOGGLE_E10S) &&
Services.prefs.getBoolPref(PREF_TOGGLE_E10S) == false);
let multi = Services.prefs.getIntPref(PREF_E10S_MULTI_OPTOUT, 0) >=
Services.appinfo.E10S_MULTI_EXPERIMENT;
return { e10s, multi };
}
/* If this function returns a non-empty string, it
* means that this particular user should be temporarily
* disqualified due to some particular reason.
* If a user shouldn't be disqualified, then an empty
* string must be returned.
*/
function getTemporaryDisqualification() {
return "";
}
/* If this function returns a non-empty string, it
* means that this particular user should be temporarily
* qualified due to some particular reason.
* If a user shouldn't be qualified, then an empty
* string must be returned.
*/
function getTemporaryQualification() {
// Whenever the DevTools toolbox is opened for the first time in a release, it
// records this fact in the following pref as part of the DevTools telemetry
// system. If this pref is set, then it means the user has opened DevTools at
// some point in time.
const PREF_OPENED_DEVTOOLS = "devtools.telemetry.tools.opened.version";
let hasOpenedDevTools = Services.prefs.prefHasUserValue(PREF_OPENED_DEVTOOLS);
if (hasOpenedDevTools) {
return "devtools";
}
return "";
}
function getAddonsDisqualifyForMulti() {
return Services.prefs.getBoolPref("extensions.e10sMultiBlocksEnabling", false) &&
Services.prefs.getBoolPref("extensions.e10sMultiBlockedByAddons", false);
}

Просмотреть файл

@ -1,32 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>e10srollout@mozilla.org</em:id>
<em:version>3.05</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<!-- Target Application this theme can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Multi-process staged rollout</em:name>
<em:description>Staged rollout of Firefox multi-process feature.</em:description>
</Description>
</RDF>

Просмотреть файл

@ -1,19 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Content Processes")
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
FINAL_TARGET_FILES.features['e10srollout@mozilla.org'] += [
'bootstrap.js'
]
FINAL_TARGET_PP_FILES.features['e10srollout@mozilla.org'] += [
'install.rdf.in'
]

Просмотреть файл

@ -154,10 +154,11 @@ FormAutofillHandler.prototype = {
this.address.fieldDetails = [];
}
if (!this.creditCard.fieldDetails.some(i => i.fieldName == "cc-number")) {
log.debug("Ignoring credit card related fields since it's without credit card number field");
if (!this._isValidCreditCardForm(this.creditCard.fieldDetails)) {
log.debug("Invalid credit card form");
this.creditCard.fieldDetails = [];
}
let validDetails = Array.of(...(this.address.fieldDetails),
...(this.creditCard.fieldDetails));
for (let detail of validDetails) {
@ -171,6 +172,28 @@ FormAutofillHandler.prototype = {
return validDetails;
},
_isValidCreditCardForm(fieldDetails) {
let ccNumberReason = "";
let hasCCNumber = false;
let hasExpiryDate = false;
for (let detail of fieldDetails) {
switch (detail.fieldName) {
case "cc-number":
hasCCNumber = true;
ccNumberReason = detail._reason;
break;
case "cc-exp":
case "cc-exp-month":
case "cc-exp-year":
hasExpiryDate = true;
break;
}
}
return hasCCNumber && (ccNumberReason == "autocomplete" || hasExpiryDate);
},
getFieldDetailByName(fieldName) {
return this.fieldDetails.find(detail => detail.fieldName == fieldName);
},

Просмотреть файл

@ -391,14 +391,17 @@ this.FormAutofillUtils = {
},
findCreditCardSelectOption(selectEl, creditCard, fieldName) {
let oneDigitMonth = creditCard["cc-exp-month"].toString();
let twoDigitsMonth = oneDigitMonth.padStart(2, "0");
let fourDigitsYear = creditCard["cc-exp-year"].toString();
let twoDigitsYear = fourDigitsYear.substr(2, 2);
let oneDigitMonth = creditCard["cc-exp-month"] ? creditCard["cc-exp-month"].toString() : null;
let twoDigitsMonth = oneDigitMonth ? oneDigitMonth.padStart(2, "0") : null;
let fourDigitsYear = creditCard["cc-exp-year"] ? creditCard["cc-exp-year"].toString() : null;
let twoDigitsYear = fourDigitsYear ? fourDigitsYear.substr(2, 2) : null;
let options = Array.from(selectEl.options);
switch (fieldName) {
case "cc-exp-month": {
if (!oneDigitMonth) {
return null;
}
for (let option of options) {
if ([option.text, option.label, option.value].some(s => {
let result = /[1-9]\d*/.exec(s);
@ -410,6 +413,9 @@ this.FormAutofillUtils = {
break;
}
case "cc-exp-year": {
if (!fourDigitsYear) {
return null;
}
for (let option of options) {
if ([option.text, option.label, option.value].some(
s => s == twoDigitsYear || s == fourDigitsYear
@ -420,6 +426,9 @@ this.FormAutofillUtils = {
break;
}
case "cc-exp": {
if (!oneDigitMonth || !fourDigitsYear) {
return null;
}
let patterns = [
oneDigitMonth + "/" + twoDigitsYear, // 8/22
oneDigitMonth + "/" + fourDigitsYear, // 8/2022

Просмотреть файл

@ -149,7 +149,9 @@ xul|richlistitem[originaltype="autofill-insecureWarning"] {
}
.autofill-footer > .autofill-option-button {
height: 40px;
box-sizing: border-box;
padding: 0 10px;
min-height: 40px;
background-color: #EDEDED;
font-size: calc(var(--option-btn-font-size) / var(--default-font-size) * 1em);
color: var(--option-btn-text-color);

Просмотреть файл

@ -159,10 +159,18 @@ const TESTCASES = [
],
},
{
description: "It's an invalid address and credit form.",
description: "An invalid address form due to less than 3 fields.",
document: `<form>
<input id="given-name" autocomplete="shipping given-name">
<input autocomplete="shipping address-level2">
</form>`,
addressFieldDetails: [],
creditCardFieldDetails: [],
validFieldDetails: [],
},
{
description: "An invalid credit card form due to omitted cc-number.",
document: `<form>
<input id="cc-name" autocomplete="cc-name">
<input id="cc-exp-month" autocomplete="cc-exp-month">
<input id="cc-exp-year" autocomplete="cc-exp-year">
@ -171,6 +179,73 @@ const TESTCASES = [
creditCardFieldDetails: [],
validFieldDetails: [],
},
{
description: "An invalid credit card form due to non-autocomplete-attr cc-number and omitted cc-exp-*.",
document: `<form>
<input id="cc-name" autocomplete="cc-name">
<input id="cc-number" name="card-number">
</form>`,
addressFieldDetails: [],
creditCardFieldDetails: [],
validFieldDetails: [],
},
{
description: "A valid credit card form with autocomplete-attr cc-number only.",
document: `<form>
<input id="cc-number" autocomplete="cc-number">
</form>`,
addressFieldDetails: [],
creditCardFieldDetails: [
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
],
validFieldDetails: [
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
],
},
{
description: "A valid credit card form with non-autocomplete-attr cc-number and cc-exp.",
document: `<form>
<input id="cc-number" name="card-number">
<input id="cc-exp" autocomplete="cc-exp">
</form>`,
addressFieldDetails: [],
creditCardFieldDetails: [
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp"},
],
validFieldDetails: [
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp"},
],
ids: [
"cc-number",
"cc-exp",
],
},
{
description: "A valid credit card form with non-autocomplete-attr cc-number and cc-exp-month/cc-exp-year.",
document: `<form>
<input id="cc-number" name="card-number">
<input id="cc-exp-month" autocomplete="cc-exp-month">
<input id="cc-exp-year" autocomplete="cc-exp-year">
</form>`,
addressFieldDetails: [],
creditCardFieldDetails: [
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
],
validFieldDetails: [
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
],
ids: [
"cc-number",
"cc-exp-month",
"cc-exp-year",
],
},
{
description: "Three sets of adjacent phone number fields",
document: `<form>

Просмотреть файл

@ -658,6 +658,82 @@ const TESTCASES = [
expectedResult: [DEFAULT_CREDITCARD_RECORD],
expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
},
{
description: "Fill a cc-exp without cc-exp-month value in the profile",
document: `<form><select autocomplete="cc-exp">
<option value="03/17">03/17</option>
<option value="01/25">01/25</option>
</select></form>`,
profileData: [Object.assign({}, {
"guid": "123",
"cc-exp-year": 2025,
})],
expectedResult: [{
"guid": "123",
"cc-exp-year": 2025,
}],
expectedOptionElements: [],
},
{
description: "Fill a cc-exp without cc-exp-year value in the profile",
document: `<form><select autocomplete="cc-exp">
<option value="03/17">03/17</option>
<option value="01/25">01/25</option>
</select></form>`,
profileData: [Object.assign({}, {
"guid": "123",
"cc-exp-month": 1,
})],
expectedResult: [{
"guid": "123",
"cc-exp-month": 1,
}],
expectedOptionElements: [],
},
{
description: "Fill a cc-exp* without cc-exp-month value in the profile",
document: `<form>
<select autocomplete="cc-exp-month">
<option value="03">03</option>
<option value="01">01</option>
</select>
<select autocomplete="cc-exp-year">
<option value="17">2017</option>
<option value="25">2025</option>
</select>
</form>`,
profileData: [Object.assign({}, {
"guid": "123",
"cc-exp-year": 2025,
})],
expectedResult: [{
"guid": "123",
"cc-exp-year": 2025,
}],
expectedOptionElements: [],
},
{
description: "Fill a cc-exp* without cc-exp-year value in the profile",
document: `<form>
<select autocomplete="cc-exp-month">
<option value="03">03</option>
<option value="01">01</option>
</select>
<select autocomplete="cc-exp-year">
<option value="17">2017</option>
<option value="25">2025</option>
</select>
</form>`,
profileData: [Object.assign({}, {
"guid": "123",
"cc-exp-month": 1,
})],
expectedResult: [{
"guid": "123",
"cc-exp-month": 1,
}],
expectedOptionElements: [],
},
];
for (let testcase of TESTCASES) {

Просмотреть файл

@ -7,7 +7,6 @@
DIRS += [
'activity-stream',
'aushelper',
'e10srollout',
'followonsearch',
'formautofill',
'onboarding',

Просмотреть файл

@ -0,0 +1,201 @@
/* 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";
this.EXPORTED_SYMBOLS = ["OnboardingTelemetry"];
const {utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
PingCentre: "resource:///modules/PingCentre.jsm",
Services: "resource://gre/modules/Services.jsm",
});
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
/**
* We send 2 kinds (firefox-onboarding-event, firefox-onboarding-session) of pings to ping centre
* server (they call it `topic`). The `internal` state in `topic` field means this event is used internaly to
* track states and will not send out any message.
*
* To save server space and make query easier, we track session begin and end but only send pings
* when session end. Therefore the server will get single "onboarding/overlay/notification-session"
* event which includes both `session_begin` and `session_end` timestamp.
*
* We send `session_begin` and `session_end` timestamps instead of `session_duration` diff because
* of analytics engineer's request.
*/
const EVENT_WHITELIST = {
// track when click the notification close button
"notification-close-button-click": {topic: "firefox-onboarding-event", category: "notification-interactions"},
// track when click the notification Call-To-Action button
"notification-cta-click": {topic: "firefox-onboarding-event", category: "notification-interactions"},
// track when notification is shown
"notification-session-begin": {topic: "internal"},
// track when the notification closed
"notification-session-end": {topic: "firefox-onboarding-session", category: "notification-interactions"},
// init onboarding session with session_key and page url
"onboarding-register-session": {topic: "internal"},
// track when the onboarding script inited
"onboarding-session-begin": {topic: "internal"},
// track when the onboarding script destoryed
"onboarding-session-end": {topic: "firefox-onboarding-session", category: "overlay-interactions"},
// track when click the overlay Call-To-Action button
"overlay-cta-click": {topic: "firefox-onboarding-event", category: "overlay-interactions"},
// track when click or auto select the overlay navigate item
"overlay-nav-click": {topic: "firefox-onboarding-event", category: "overlay-interactions"},
// track when the overlay is shown
"overlay-session-begin": {topic: "internal"},
// track when the overlay is closed
"overlay-session-end": {topic: "firefox-onboarding-session", category: "overlay-interactions"},
// track when click the overlay "skip tour" button
"overlay-skip-tour": {topic: "firefox-onboarding-event", category: "overlay-interactions"},
};
const ONBOARDING_STATE_DEFAULT = "default";
const ONBOARDING_ID = "onboarding";
let OnboardingTelemetry = {
sessionProbe: null,
eventProbe: null,
state: {
sessions: {},
},
init(startupData) {
this.sessionProbe = new PingCentre({topic: "firefox-onboarding-session"});
this.eventProbe = new PingCentre({topic: "firefox-onboarding-event"});
this.state.addon_version = startupData.version;
},
// register per tab session data
registerNewTelemetrySession(session_key, page) {
if (this.state.sessions[session_key]) {
return;
}
// session_key and page url are must have
if (!session_key || !page) {
throw new Error("session_key and page url are required for onboarding-register-session");
}
let session_id = gUUIDGenerator.generateUUID().toString();
this.state.sessions[session_key] = {
session_id,
page,
};
},
process(data) {
let { event, page, session_key } = data;
let topic = EVENT_WHITELIST[event] && EVENT_WHITELIST[event].topic;
if (!topic) {
throw new Error(`ping-centre doesn't know ${event}, only knows ${Object.keys(EVENT_WHITELIST)}`);
}
if (event === "onboarding-register-session") {
this.registerNewTelemetrySession(session_key, page);
}
if (!this.state.sessions[session_key]) {
throw new Error(`should pass valid session_key`);
}
if (topic === "internal") {
switch (event) {
case "onboarding-session-begin":
this.state.sessions[session_key].onboarding_session_begin = Date.now();
break;
case "overlay-session-begin":
this.state.sessions[session_key].overlay_session_begin = Date.now();
break;
case "notification-session-begin":
this.state.sessions[session_key].notification_session_begin = Date.now();
break;
}
} else {
this._send(topic, data);
}
},
// send out pings by topic
_send(topic, data) {
let {
addon_version,
} = this.state;
let {
event,
tour_id = "",
session_key,
} = data;
let {
notification_session_begin,
onboarding_session_begin,
overlay_session_begin,
page,
session_id,
} = this.state.sessions[session_key];
let category = EVENT_WHITELIST[event].category;
let session_begin;
switch (topic) {
case "firefox-onboarding-session":
switch (event) {
case "onboarding-session-end":
if (!onboarding_session_begin) {
throw new Error(`should fire onboarding-session-begin event before ${event}`);
}
event = "onboarding-session";
session_begin = onboarding_session_begin;
delete this.state.sessions[session_key];
break;
case "overlay-session-end":
if (!overlay_session_begin) {
throw new Error(`should fire overlay-session-begin event before ${event}`);
}
event = "overlay-session";
session_begin = overlay_session_begin;
break;
case "notification-session-end":
if (!notification_session_begin) {
throw new Error(`should fire notification-session-begin event before ${event}`);
}
event = "notification-session";
session_begin = notification_session_begin;
break;
}
// the field is used to identify how user open the overlay (through default logo or watermark),
// the number of open from notification can be retrieved via `notification-cta-click` event
const tour_source = Services.prefs.getStringPref("browser.onboarding.state",
ONBOARDING_STATE_DEFAULT);
const session_end = Date.now();
this.sessionProbe && this.sessionProbe.sendPing({
addon_version,
category,
event,
page,
session_begin,
session_end,
session_id,
tour_id,
tour_source,
}, {filter: ONBOARDING_ID});
break;
case "firefox-onboarding-event":
let impression = (event === "notification-close-button-click" ||
event === "notification-cta-click") ?
Services.prefs.getIntPref("browser.onboarding.notification.prompt-count", 0) : -1;
this.eventProbe && this.eventProbe.sendPing({
addon_version,
category,
event,
impression,
page,
session_id,
tour_id,
}, {filter: ONBOARDING_ID});
break;
}
}
};

Просмотреть файл

@ -24,9 +24,13 @@ Snippets (the remote notification that handled by activity stream) will only be
## Architecture
Everytime `about:home` or `about:newtab` page is opened, onboarding overlay is injected into that page.
![](https://i.imgur.com/7RK89Zw.png)
`OnboardingTourType.jsm` will check the onboarding tour type (currently support `new` and `update`). Then in `onboarding.js`, All tours are defined inside of `onboardingTourset` dictionary. `getTourIDList` function will load tours from proper preferences. (Check `How to change the order of tours` section for more detail).
During booting from `bootstrap.js`, `OnboardingTourType.jsm` will check the onboarding tour type (`new` and `update` are supported types) and set required initial states into preferences.
Everytime `about:home` or `about:newtab` page is opened, `onboarding.js` is injected into that page via [frame scripts](https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Communicating_with_frame_scripts).
Then in `onboarding.js`, all tours are defined inside of `onboardingTourset` dictionary. `getTourIDList` function will load tours from proper preferences. (Check `How to change the order of tours` section for more detail).
When user clicks the action button in each tour, We use [UITour](http://bedrock.readthedocs.io/en/latest/uitour.html) to highlight the correspondent browser UI element. The UITour client is bundled in onboarding addon via `jar.mn`.

28
browser/extensions/onboarding/bootstrap.js поставляемый
Просмотреть файл

@ -7,12 +7,12 @@
const {utils: Cu, interfaces: Ci} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OnboardingTourType",
"resource://onboarding/modules/OnboardingTourType.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
OnboardingTourType: "resource://onboarding/modules/OnboardingTourType.jsm",
OnboardingTelemetry: "resource://onboarding/modules/OnboardingTelemetry.jsm",
Services: "resource://gre/modules/Services.jsm",
fxAccounts: "resource://gre/modules/FxAccounts.jsm",
});
const {PREF_STRING, PREF_BOOL, PREF_INT} = Ci.nsIPrefBranch;
@ -40,6 +40,7 @@ const PREF_WHITELIST = [
].forEach(tourId => PREF_WHITELIST.push([`browser.onboarding.tour.${tourId}.completed`, PREF_BOOL]));
let waitingForBrowserReady = true;
let startupData;
/**
* Set pref. Why no `getPrefs` function is due to the priviledge level.
@ -64,15 +65,12 @@ function setPrefs(prefs) {
case PREF_BOOL:
Services.prefs.setBoolPref(name, pref.value);
break;
case PREF_INT:
Services.prefs.setIntPref(name, pref.value);
break;
case PREF_STRING:
Services.prefs.setStringPref(name, pref.value);
break;
default:
throw new TypeError(`Unexpected type (${type}) for preference ${name}.`);
}
@ -155,6 +153,13 @@ function initContentMessageListener() {
isLoggedIn: syncTourChecker.isLoggedIn()
});
break;
case "ping-centre":
try {
OnboardingTelemetry.process(msg.data.params.data);
} catch (e) {
Cu.reportError(e);
}
break;
}
});
}
@ -166,6 +171,7 @@ function onBrowserReady() {
waitingForBrowserReady = false;
OnboardingTourType.check();
OnboardingTelemetry.init(startupData);
Services.mm.loadFrameScript("resource://onboarding/onboarding.js", true);
initContentMessageListener();
}
@ -195,6 +201,9 @@ function install(aData, aReason) {}
function uninstall(aData, aReason) {}
function startup(aData, aReason) {
// Cache startup data which contains stuff like the version number, etc.
// so we can use it when we init the telemetry
startupData = aData;
// Only start Onboarding when the browser UI is ready
if (Services.startup.startingUp) {
Services.obs.addObserver(observe, BROWSER_READY_NOTIFICATION);
@ -206,6 +215,7 @@ function startup(aData, aReason) {
}
function shutdown(aData, aReason) {
startupData = null;
// Stop waiting for browser to be ready
if (waitingForBrowserReady) {
Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);

Просмотреть файл

@ -7,7 +7,6 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const ONBOARDING_CSS_URL = "resource://onboarding/onboarding.css";
@ -333,13 +332,28 @@ var onboardingTourset = {
/**
* @param {String} action the action to ask the chrome to do
* @param {Array} params the parameters for the action
* @param {Array | Object} params the parameters for the action
*/
function sendMessageToChrome(action, params) {
sendAsyncMessage("Onboarding:OnContentMessage", {
action, params
});
}
/**
* Template code for talking to `PingCentre`
* @param {Object} data the payload for the telemetry
*/
function telemetry(data) {
sendMessageToChrome("ping-centre", {data});
}
function registerNewTelemetrySession(data) {
telemetry(Object.assign(data, {
event: "onboarding-register-session",
}));
}
/**
* The script won't be initialized if we turned off onboarding by
* setting "browser.onboarding.enabled" to false.
@ -351,6 +365,9 @@ class Onboarding {
async init(contentWindow) {
this._window = contentWindow;
// session_key is used for telemetry to track the current tab.
// The number will renew after reload the page.
this._session_key = Date.now();
this._tours = [];
this._tourType = Services.prefs.getStringPref("browser.onboarding.tour-type", "update");
@ -381,6 +398,14 @@ class Onboarding {
this.uiInitialized = false;
this._resizeTimerId =
this._window.requestIdleCallback(() => this._resizeUI());
registerNewTelemetrySession({
page: this._window.location.href,
session_key: this._session_key,
});
telemetry({
event: "onboarding-session-begin",
session_key: this._session_key,
});
}
_resizeUI() {
@ -525,13 +550,24 @@ class Onboarding {
this.hideOverlay();
break;
case "onboarding-notification-close-btn":
let tour_id = this._notificationBar.dataset.targetTourId;
this.hideNotification();
this._removeTourFromNotificationQueue(this._notificationBar.dataset.targetTourId);
this._removeTourFromNotificationQueue(tour_id);
telemetry({
event: "notification-close-button-click",
tour_id,
session_key: this._session_key,
});
break;
case "onboarding-notification-action-btn":
let tourId = this._notificationBar.dataset.targetTourId;
this.showOverlay();
this.gotoPage(tourId);
telemetry({
event: "notification-cta-click",
tour_id: tourId,
session_key: this._session_key,
});
this._removeTourFromNotificationQueue(tourId);
break;
}
@ -543,6 +579,11 @@ class Onboarding {
} else if (classList.contains("onboarding-tour-action-button")) {
let activeItem = this._tourItems.find(item => item.classList.contains("onboarding-active"));
this.setToursCompleted([ activeItem.id ]);
telemetry({
event: "overlay-cta-click",
tour_id: activeItem.id,
session_key: this._session_key,
});
}
}
@ -671,6 +712,10 @@ class Onboarding {
}
this._tourItems = this._tourPages =
this._overlayIcon = this._overlay = this._notificationBar = null;
telemetry({
event: "onboarding-session-end",
session_key: this._session_key,
});
}
_onIconStateChange(state) {
@ -693,10 +738,18 @@ class Onboarding {
this.hideNotification();
this.toggleModal(this._overlay.classList.toggle("onboarding-opened"));
telemetry({
event: "overlay-session-begin",
session_key: this._session_key
});
}
hideOverlay() {
this.toggleModal(this._overlay.classList.toggle("onboarding-opened"));
telemetry({
event: "overlay-session-end",
session_key: this._session_key,
});
}
/**
@ -748,6 +801,11 @@ class Onboarding {
if (tab.id == tourId) {
tab.classList.add("onboarding-active");
tab.setAttribute("aria-selected", true);
telemetry({
event: "overlay-nav-click",
tour_id: tourId,
session_key: this._session_key,
});
} else {
tab.classList.remove("onboarding-active");
tab.setAttribute("aria-selected", false);
@ -760,6 +818,12 @@ class Onboarding {
case "onboarding-tour-sync":
case "onboarding-tour-performance":
this.setToursCompleted([tourId]);
// also track auto completed tour so we can filter data with the same event
telemetry({
event: "overlay-cta-click",
tour_id: tourId,
session_key: this._session_key,
});
break;
}
}
@ -1001,11 +1065,22 @@ class Onboarding {
});
}
sendMessageToChrome("set-prefs", params);
telemetry({
event: "notification-session-begin",
session_key: this._session_key
});
}
hideNotification() {
if (this._notificationBar) {
this._notificationBar.classList.remove("onboarding-opened");
if (this._notificationBar.classList.contains("onboarding-opened")) {
this._notificationBar.classList.remove("onboarding-opened");
telemetry({
event: "notification-session-end",
tour_id: this._notificationBar.dataset.targetTourId,
session_key: this._session_key,
});
}
}
}
@ -1046,6 +1121,10 @@ class Onboarding {
value: ICON_STATE_WATERMARK
}
]);
telemetry({
event: "overlay-skip-tour",
session_key: this._session_key
});
}
_renderOverlay() {

Просмотреть файл

@ -0,0 +1,116 @@
# Metrics we collect
We adhere to [Activity Stream's data collection policy](https://github.com/mozilla/activity-stream/blob/master/docs/v2-system-addon/data_events.md). Data about your specific browsing behavior or the sites you visit is **never transmitted to any Mozilla server**. At any time, it is easy to **turn off** this data collection by [opting out of Firefox telemetry](https://support.mozilla.org/kb/share-telemetry-data-mozilla-help-improve-firefox).
## User event pings
The Onboarding system add-on sends 2 types of pings(HTTPS POST) to the backend [Onyx server](https://github.com/mozilla/onyx) :
- a `session` ping that describes the ending of an Onboarding session (a new tab is closed or refreshed, an overlay is closed, a notification bar is closed), and
- an `event` ping that records specific data about individual user interactions while interacting with Onboarding
For reference, Onyx is a Mozilla owned service to serve tiles for the current newtab in Firefox. It also receives all the telemetry from the about:newtab and about:home page as well as Activity Stream. It's operated and monitored by the Cloud Services team.
# Example Onboarding `session` Log
```js
{
// These fields are sent from the client
"addon_version": "1.0.0",
"category": ["overlay-interactions"|"notification-interactions"],
"client_id": "374dc4d8-0cb2-4ac5-a3cf-c5a9bc3c602e",
"locale": "en-US",
"event": ["onboarding_session" | "overlay_session" | "notification_session"],
"page": ["about:newtab" | "about:home"],
"session_begin": 1505440017018,
"session_end": 1505440021992,
"session_id": "{12dasd-213asda-213dkakj}",
"tour_source": ["default" | "watermark"],
"tour_id": ["onboarding-tour-private-browsing" | "onboarding-tour-addons"|...], // tour ids defined in 'onboardingTourset'
// These fields are generated on the server
"date": "2016-03-07",
"ip": "10.192.171.13",
"ua": "python-requests/2.9.1",
"receive_at": 1457396660000
}
```
# Example Onboarding `event` Log
```js
{
"addon_version": "1.0.0",
"category": ["overlay-interactions"|"notification-interactions"],
"client_id": "374dc4d8-0cb2-4ac5-a3cf-c5a9bc3c602e",
"event": ["notification-cta-click" | "overlay-cta-click" | "overlay-nav-click" | "overlay-skip-tour"],
"impression": [0-8],
"locale": "en-US",
"page": ["about:newtab" | "about:home"],
"session_id": "{12dasd-213asda-213dkakj}",
"tour_id": ["onboarding-tour-private-browsing" | "onboarding-tour-addons"|...], // tour ids defined in 'onboardingTourset'
// These fields are generated on the server
"ip": "10.192.171.13",
"ua": "python-requests/2.9.1",
"receive_at": 1457396660000,
"date": "2016-03-07",
}
```
| KEY | DESCRIPTION | &nbsp; |
|-----|-------------|:-----:|
| `addon_version` | [Required] The version of the Onboarding addon. | :one:
| `category` | [Required] Either ("overlay-interactions", "notification-interactions") to identify which kind of the interaction | :one:
| `client_id` | [Required] An identifier generated by [ClientID](https://github.com/mozilla/gecko-dev/blob/master/toolkit/modules/ClientID.jsm) module to provide an identifier for this device. Auto append by `ping-centre` module | :one:
| `event` | [Required] The type of event. allowed event strings are defined in the below section | :one:
| `impression` | [Optional] An integer to record how many times the current notification tour is shown to the user. Each Notification tour can show not more than 8 times. We put `-1` when this field is not relevant to this event | :one:
| `ip` | [Auto populated by Onyx] The IP address of the client. Onyx does use (with the permission) the IP address to infer user's geo information so that it could prepare the corresponding tiles for the country she lives in. However, Ping-centre will NOT store IP address in the database, where only authorized Mozilla employees can access the telemetry data, and all the raw logs are being strictly managed by the Ops team and will expire according to the Mozilla's data retention policy.| :two:
| `locale` | The browser chrome's language (eg. en-US). | :two:
| `page` | [Required] One of ["about:newtab", "about:home"]| :one:
| `session_begin` | Timestamp in (integer) milliseconds when onboarding/overlay/notification becoming visible. | :one:
| `session_end` | Timestamp in (integer) milliseconds when onboarding/overlay/notification losing focus. | :one:
| `session_id` | [Required] The unique identifier generated by `gUUIDGenerator` service to identify the specific user session when onboarding is inited/when overlay is opened/when notification is shown. | :one:
| `tour_source` | Either ("default", "watermark") indicates the overlay is opened while in the default or the watermark state. Open from the notification bar is counted via 'notification-cta-click event'. | :two:
| `tour_id` | id of the current tour. The number of open from notification can be retrieved via 'notification-cta-click event'. We put ` ` when this field is not relevant to this event | :two:
| `ua` | [Auto populated by Onyx] The user agent string. | :two:
| `ver` | [Auto populated by Onyx] The version of the Onyx API the ping was sent to. | :one:
**Where:**
:one: Firefox data
:two: HTTP protocol data
## Events
Here are all allowed `event` strings that defined in `OnboardingTelemetry::EVENT_WHITELIST`.
### Session events
| EVENT | DESCRIPTION |
|-----------|---------------------|
| `onboarding-register-session` | internal event triggered to register a new page session. Called when the onboarding script is inited in a browser tab. Will not send out any data. |
| `onboarding-session-begin` | internal event triggered when the onboarding script is inited, will not send out any data. |
| `onboarding-session-end` | internal event triggered when the onboarding script is destoryed. `onboarding-session` event is sent to the server. |
| `onboarding-session` | event is sent when the onboarding script is destoryed |
### Overlay events
| EVENT | DESCRIPTION |
|-----------|---------------------|
| `overlay-session-begin` | internal event triggered when user open the overlay, will not send out any data. |
| `overlay-session-end` | internal event is triggered when user close the overlay. `overlay-session` event is sent to the server. |
| `overlay-session` | event is sent when user close the overlay |
| `overlay-nav-click` | event is sent when click or auto select the overlay navigate item |
| `overlay-cta-click` | event is sent when user click the overlay CTA button |
| `overlay-skip-tour` | event is sent when click the overlay `skip tour` button |
### Notification events
| EVENT | DESCRIPTION |
|-----------|---------------------|
| `notification-session-begin` | internal event triggered when user open the notification, will not send out any data. |
| `notification-session-end` | internal event is triggered when user close the notification. `notification-session` event is sent to the server. |
| `notification-session` | event is sent when user close the notification |
| `notification-close-button-click` | event is sent when click the notification close button |
| `notification-cta-click` | event is sent when click the notification CTA button |

Просмотреть файл

@ -11,7 +11,7 @@
<em:id>onboarding@mozilla.org</em:id>
<em:name>Photon onboarding</em:name>
<em:description>Photon onboarding</em:description>
<em:version>0.1</em:version>
<em:version>1.0</em:version>
<em:bootstrap>true</em:bootstrap>
<em:type>2</em:type>
<em:multiprocessCompatible>true</em:multiprocessCompatible>

Просмотреть файл

@ -7,10 +7,6 @@
<!ENTITY blockPopups.label "Block pop-up windows">
<!ENTITY blockPopups.accesskey "B">
<!ENTITY notificationsDoNotDisturb.label "Do not disturb me">
<!ENTITY notificationsDoNotDisturb.accesskey "n">
<!ENTITY notificationsDoNotDisturbDetails.value "No notification will be shown until you restart &brandShortName;">
<!ENTITY popupExceptions.label "Exceptions…">
<!ENTITY popupExceptions.accesskey "E">

Просмотреть файл

@ -40,6 +40,11 @@ invalidURITitle=Invalid Hostname Entered
savedLoginsExceptions_title=Exceptions - Saved Logins
savedLoginsExceptions_desc3=Logins for the following websites will not be saved
# LOCALIZATION NOTE(pauseNotifications.label): %S is replaced with the
# brandShortName of the application.
pauseNotifications.label=Pause notifications until %S restarts
pauseNotifications.accesskey=n
#### Block List Manager
blockliststext=You can choose which list Firefox will use to block Web elements that may track your browsing activity.

Просмотреть файл

@ -8,6 +8,7 @@ def test(mod, path, entity = None):
if mod not in ("netwerk", "dom", "toolkit", "security/manager",
"devtools/client", "devtools/shared", "devtools/shim",
"browser",
"browser/extensions/formautofill",
"browser/extensions/onboarding",
"browser/extensions/webcompat-reporter",
"extensions/spellcheck",

Просмотреть файл

@ -12,6 +12,7 @@ dirs = browser
browser/branding/official
devtools/client
devtools/shim
browser/extensions/formautofill
browser/extensions/onboarding
browser/extensions/webcompat-reporter

Просмотреть файл

@ -119,6 +119,10 @@ locales = [
reference = "browser/branding/official/locales/en-US/**"
l10n = "{l}browser/branding/official/**"
[[paths]]
reference = "browser/extensions/formautofill/locales/en-US/**"
l10n = "{l}browser/extensions/formautofill/**"
[[paths]]
reference = "browser/extensions/onboarding/locales/en-US/**"
l10n = "{l}browser/extensions/onboarding/**"

Просмотреть файл

@ -89,8 +89,12 @@
margin-left: 7px;
}
.titlebar-placeholder[type="fullscreen-button"] {
margin-right: 4px;
/* The fullscreen button doesnt show on Yosemite(10.10) or above so dont give it a
border there */
@media not all and (-moz-mac-yosemite-theme) {
.titlebar-placeholder[type="fullscreen-button"] {
margin-right: 4px;
}
}
#main-window:not(:-moz-lwtheme) > #titlebar {

Просмотреть файл

@ -21,20 +21,19 @@ const expectedStyle = (w, h, z) =>
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
let highlighterUtils = inspector.toolbox.highlighterUtils;
info("Highlighting the test node");
yield hoverElement("div", inspector);
let isVisible = yield testActor.isHighlighting();
ok(isVisible, "The highlighter is visible");
let div = yield getNodeFront("div", inspector);
for (let level of TEST_LEVELS) {
info("Zoom to level " + level +
" and check that the highlighter is correct");
info(`Zoom to level ${level}`);
yield testActor.zoomPageTo(level, false);
yield testActor.zoomPageTo(level);
isVisible = yield testActor.isHighlighting();
ok(isVisible, "The highlighter is still visible at zoom level " + level);
info("Highlight the test node");
yield highlighterUtils.highlightNodeFront(div);
let isVisible = yield testActor.isHighlighting();
ok(isVisible, `The highlighter is visible at zoom level ${level}`);
yield testActor.isNodeCorrectlyHighlighted("div", is);
@ -44,24 +43,13 @@ add_task(function* () {
let { width, height } = yield testActor.getWindowDimensions();
is(style, expectedStyle(width, height, level),
"The style attribute of the root element is correct");
info("Unhighlight the node");
yield highlighterUtils.unhighlight();
}
});
function* hoverElement(selector, inspector) {
info("Hovering node " + selector + " in the markup view");
let container = yield getContainerForSelector(selector, inspector);
yield hoverContainer(container, inspector);
}
function* hoverContainer(container, inspector) {
let onHighlight = inspector.toolbox.once("node-highlight");
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
inspector.markup.doc.defaultView);
yield onHighlight;
}
function* getElementsNodeStyle(testActor) {
let value = yield testActor.getHighlighterNodeAttribute(
"box-model-elements", "style");
let value = yield testActor.getHighlighterNodeAttribute("box-model-elements", "style");
return value;
}

Просмотреть файл

@ -28,7 +28,11 @@ require.config({
"devtools/client/shared/vendor/react":
JSONView.debug
? "resource://devtools-client-shared/vendor/react-dev"
: "resource://devtools-client-shared/vendor/react"
: "resource://devtools-client-shared/vendor/react",
"devtools/client/shared/vendor/react-dom":
JSONView.debug
? "resource://devtools-client-shared/vendor/react-dom-dev"
: "resource://devtools-client-shared/vendor/react-dom",
}
});

Просмотреть файл

@ -100,6 +100,10 @@ function BrowserLoaderBuilder({ baseURI, window, useOnlyShared, commonLibRequire
if (AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES) {
dynamicPaths["devtools/client/shared/vendor/react"] =
"resource://devtools/client/shared/vendor/react-dev";
dynamicPaths["devtools/client/shared/vendor/react-dom"] =
"resource://devtools/client/shared/vendor/react-dom-dev";
dynamicPaths["devtools/client/shared/vendor/react-dom-server"] =
"resource://devtools/client/shared/vendor/react-dom-server-dev";
}
const opts = {

92
devtools/client/shared/vendor/REACT_UPGRADING поставляемый
Просмотреть файл

@ -1,92 +0,0 @@
/* 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/. */
We use a version of React that has a few minor tweaks. We want to use
an un-minified production version anyway, and because of all of this
you need to build React yourself to upgrade it for devtools.
First, clone the repo and get ready to build it. Replace `<version>`
with the version tag you are targetting:
* git clone https://github.com/facebook/react.git
* cd react
* git checkout <version>
* In `src/addons/ReactWithAddons.js`, move the
`React.addons.TestUtils = ...` line outside of the `if`
block to force it be include in the production build
Next, build React:
* npm install
* grunt build
Unfortunately, you need to manually patch the generated JS file. We
need to force React to always create HTML elements, and we do this by
changing all `document.createElement` calls to `createElementNS`. It's
much easier to do this on the generated file to make sure you update
all dependencies as well.
Open `build/react-with-addons.js` and search for all
`document.createElement` calls and replace them with
`document.createElementNS('http://www.w3.org/1999/xhtml', ...)`. Note
that some code is `ownerDocument.createElement` so don't do a blind
search/replace. There is only about ~14 places to change.
Now move into our repo (note the naming of `react-dev.js`, it's the dev version):
* cp build/react-with-addons.js <gecko-dev>/devtools/client/shared/vendor/react-dev.js
Now we need to generate a production version of React:
* NODE_ENV=production grunt build
Unfortunately, you need to manually replace all the `createElement`
calls in this version again. We know this is not ideal but WE NEED TO
MOVE OFF XUL and we don't need to do this anymore once that happens.
After patching `build/react-with-addons.js` again, copy the production
version over:
* cp build/react-with-addons.js <gecko-dev>/devtools/client/shared/vendor/react.js
You also need to copy the ReactDOM and ReactDOMServer package. It requires React, so
right now we are just manually changing the path from `react` to
`devtools/client/shared/vendor/react`. Also, to have React's event system working
correctly in certain XUL situations, ReactDOM must be monkey patched with a fix. This
fix is currently applied in devtools/client/shared/vendor/react-dom.js. When upgrading,
copy and paste the existing block of code into the new file in the same location. It is
delimited by a header and footer, and then the monkeyPatchReactDOM() needs to be applied
to the returned value.
e.g. Turn this:
```
})(function(React) {
return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
});
```
Into this:
```
})(function(React) {
//--------------------------------------------------------------------------------------
// START MONKEY PATCH
...
// END MONKEY PATCH
//--------------------------------------------------------------------------------------
return monkeyPatchReactDOM(React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED);
});
```
* cp build/react-dom.js <gecko-dev>/devtools/client/shared/vendor/react-dom.js
* (change `require('react')` at the top of the file to the right path)
* Copy/paste existing monkey patch
* Apply monkeyPatchReactDOM() to the returned object ReactDOM object.
* cp build/react-dom.js <gecko-dev>/devtools/client/shared/vendor/react-dom-server.js
* (change `require('react')` at the top of the file to the right path)

407
devtools/client/shared/vendor/REACT_UPGRADING.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,407 @@
[//]: # (
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 thisfile, You can obtain one at http://mozilla.org/MPL/2.0/.
)
# Upgrading React
## Introduction
We use a version of React that has a few minor tweaks. We want to use an un-minified production version anyway, and because of all of this you need to build React yourself to upgrade it for devtools.
## Getting the Source
```
git clone https://github.com/facebook/react.git
cd react
git checkout v15.6.1 # or the version you are targetting
```
## Building
```
npm install
grunt build
```
If you did not receive any errors go to the section entitled "[Patching (XUL Workarounds)](#patching-xul-workarounds)."
If you receive the following error:
> Current npm version is not supported for development,
> expected "x.x.x." to satisfy "2.x || 3.x || 4.x"
Your npm version is too recent. `"2.x || 3.x || 4.x"` is a hint that the React project only supports npm versions 2.x, 3.x and 4.x. To fix this let's start by removing all of your node versions:
```
# If you use ubuntu
sudo apt-get remove --purge nodejs
# If you use homebrew
brew uninstall node
# If yu use macports
sudo port uninstall nodejs
# If you use nvm
LINK="https://github.com/creationix/nvm/issues/298"
xdg-open $LINK || open $LINK
```
You will need to setup a node version manager. These instructions cover "n" but many people prefer to use "nvm". If you choose to use nvm them you will need to set it up yourself.
Run the n-install script and it will set "n" it up for you:
```
curl -L -o /tmp/n-install-script https://git.io/n-install
bash /tmp/n-install-script -y
exec $SHELL # To re-initialize the PATH variable
```
To match node versions with npm versions see:
<https://nodejs.org/en/download/releases/>
The latest 4.x version of npm is installed with node 7.10.1 so install that version using `sudo n 7.10.1`.
Running `node --version` should now show v7.10.1 and `npm --version` should show 4.2.0.
Now try again:
```
npm install
grunt build
```
## Patching
### Patching build/react-with-addons.js
- Open `build/react-with-addons.js`. Search for all `document.createElement` calls and replace them with `document.createElementNS('http://www.w3.org/1999/xhtml', ...)`.
**Note**: some code may be `ownerDocument.createElement` so don't do a blind search/replace. At the time of writing there was only 1 place to change.
- If you are editing the production version then change this:
```
if ("production" !== 'production') {
exports.getReactPerf = function () {
return getReactDOM().__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactPerf;
};
exports.getReactTestUtils = function () {
return getReactDOM().__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactTestUtils;
};
}
```
To this:
```
if ("production" !== 'production') {
exports.getReactPerf = function () {
return getReactDOM().__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactPerf;
};
}
exports.getReactTestUtils = function () {
return getReactDOM().__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactTestUtils;
};
```
- If you are editing the production version then change this:
```
if ("production" !== 'production') {
// For the UMD build we get these lazily from the global since they're tied
// to the DOM renderer and it hasn't loaded yet.
Object.defineProperty(React.addons, 'Perf', {
enumerable: true,
get: function () {
return ReactAddonsDOMDependencies.getReactPerf();
}
});
Object.defineProperty(React.addons, 'TestUtils', {
enumerable: true,
get: function () {
return ReactAddonsDOMDependencies.getReactTestUtils();
}
});
}
```
To this:
```
if ("production" !== 'production') {
// For the UMD build we get these lazily from the global since they're tied
// to the DOM renderer and it hasn't loaded yet.
Object.defineProperty(React.addons, 'Perf', {
enumerable: true,
get: function () {
return ReactAddonsDOMDependencies.getReactPerf();
}
});
}
Object.defineProperty(React.addons, 'TestUtils', {
enumerable: true,
get: function () {
return ReactAddonsDOMDependencies.getReactTestUtils();
}
});
```
### Patching build/react-dom.js
- Open `build/react-dom.js` and replace all of the document.createElement calls as you did for `build/react-with-addons.js`.
- Change `require('react')` near the top of the file to `require('devtools/client/shared/vendor/react')`.
- About four lines below that require there is a `define(['react'], f);`. Change this to the full path e.g.`define(['devtools/client/shared/vendor/react'], f);`
- If you are editing the production version then change this:
```
if ("production" !== 'production') {
ReactDOMUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
// ReactPerf and ReactTestUtils currently only work with the DOM renderer
// so we expose them from here, but only in DEV mode.
ReactPerf: _dereq_(71),
ReactTestUtils: _dereq_(80)
};
}
```
Into this:
```
if ("production" !== 'production') {
ReactDOMUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
// ReactPerf and ReactTestUtils currently only work with the DOM renderer
// so we expose them from here, but only in DEV mode.
ReactPerf: _dereq_(71)
};
}
ReactDOMUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
ReactTestUtils: _dereq_(80)
}
```
To have React's event system working correctly in certain XUL situations, ReactDOM must be monkey patched with a fix. This fix is currently applied in devtools/client/shared/vendor/react-dom.js. When upgrading, copy and paste the existing block of code into the new file in the same location. It is delimited by a header and footer, and then the monkeyPatchReactDOM() needs to be applied to the returned value.
e.g. Turn this:
```
module.exports = ReactDOM;
```
Into this:
```
//--------------------------------------------------------------------------------------
// START MONKEY PATCH
/**
* This section contains a monkey patch for React DOM, so that it functions correctly in
* certain XUL situations. React centralizes events to specific DOM nodes by only
* binding a single listener to the document of the page. It then captures these events,
* and then uses a SyntheticEvent system to dispatch these throughout the page.
*
* In privileged XUL with a XUL iframe, and React in both documents, this system breaks.
* By design, these XUL frames can still talk to each other, while in a normal HTML
* situation, they would not be able to. The events from the XUL iframe propagate to the
* parent document as well. This leads to the React event system incorrectly dispatching
* TWO SyntheticEvents for for every ONE action.
*
* The fix here is trick React into thinking that the owning document for every node in
* a XUL iframe to be the toolbox.xul. This is done by creating a Proxy object that
* captures any usage of HTMLElement.ownerDocument, and then passing in the toolbox.xul
* document rather than (for example) the netmonitor.xul document. React will then hook
* up the event system correctly on the top level controlling document.
*
* @return {object} The proxied and monkey patched ReactDOM
*/
function monkeyPatchReactDOM(ReactDOM) {
// This is the actual monkey patched function.
const reactDomRender = monkeyPatchRender(ReactDOM);
// Proxied method calls might need to be bound, but do this lazily with caching.
const lazyFunctionBinding = functionLazyBinder();
// Create a proxy, but the render property is not writable on the ReactDOM object, so
// a direct proxy will fail with an error. Instead, create a proxy on a a blank object.
// Pass on getting and setting behaviors.
return new Proxy({}, {
get: (target, name) => {
return name === "render"
? reactDomRender
: lazyFunctionBinding(ReactDOM, name);
},
set: (target, name, value) => {
ReactDOM[name] = value;
return true;
}
});
};
/**
* Creates a function that replaces the ReactDOM.render method. It does this by
* creating a proxy for the dom node being mounted. This proxy rewrites the
* "ownerDocument" property to point to the toolbox.xul document. This function
* is only used for XUL iframes inside of a XUL document.
*
* @param {object} ReactDOM
* @return {function} The patched ReactDOM.render function.
*/
function monkeyPatchRender(ReactDOM) {
const elementProxyCache = new WeakMap();
return (...args) => {
const container = args[1];
const toolboxDoc = getToolboxDocIfXulOnly(container);
if (toolboxDoc) {
// Re-use any existing cached HTMLElement proxies.
let proxy = elementProxyCache.get(container);
if (!proxy) {
// Proxied method calls need to be bound, but do this lazily.
const lazyFunctionBinding = functionLazyBinder();
// Create a proxy to the container HTMLElement. If React tries to access the
// ownerDocument, pass in the toolbox's document, as the event dispatching system
// is rooted from the toolbox document.
proxy = new Proxy(container, {
get: function (target, name) {
if (name === "ownerDocument") {
return toolboxDoc;
}
return lazyFunctionBinding(target, name);
}
});
elementProxyCache.set(container, proxy);
}
// Update the args passed to ReactDOM.render.
args[1] = proxy;
}
return ReactDOM.render.apply(this, args);
};
}
/**
* Try to access the containing toolbox XUL document, but only if all of the iframes
* in the heirarchy are XUL documents. Events dispatch differently in the case of all
* privileged XUL documents. Events that fire in an iframe propagate up to the parent
* frame. This does not happen when HTML is in the mix. Only return the toolbox if
* it matches the proper case of a XUL iframe inside of a XUL document.
*
* In addition to the XUL case, if the panel uses the toolbox's ReactDOM instance,
* this patch needs to be applied as well. This is the case for the inspector.
*
* @param {HTMLElement} node - The DOM node inside of an iframe.
* @return {XULDocument|null} The toolbox.xul document, or null.
*/
function getToolboxDocIfXulOnly(node) {
// This execution context doesn't know about XULDocuments, so don't get the toolbox.
if (typeof XULDocument !== "function") {
return null;
}
let doc = node.ownerDocument;
const inspectorUrl = "chrome://devtools/content/inspector/inspector.xhtml";
const netMonitorUrl = "chrome://devtools/content/netmonitor/netmonitor.xhtml";
const webConsoleUrl = "chrome://devtools/content/webconsole/webconsole.xhtml";
while (doc instanceof XULDocument ||
doc.location.href === inspectorUrl ||
doc.location.href === netMonitorUrl ||
doc.location.href === webConsoleUrl) {
const {frameElement} = doc.defaultView;
if (!frameElement) {
// We're at the root element, and no toolbox was found.
return null;
}
doc = frameElement.parentElement.ownerDocument;
if (doc.documentURI === "about:devtools-toolbox") {
return doc;
}
}
return null;
}
/**
* When accessing proxied functions, the instance becomes unbound to the method. This
* utility either passes a value through if it's not a function, or automatically binds a
* function and caches that bound function for repeated calls.
*/
function functionLazyBinder() {
const boundFunctions = {};
return (target, name) => {
if (typeof target[name] === "function") {
// Lazily cache function bindings.
if (boundFunctions[name]) {
return boundFunctions[name];
}
boundFunctions[name] = target[name].bind(target);
return boundFunctions[name];
}
return target[name];
};
}
// END MONKEY PATCH
//--------------------------------------------------------------------------------------
module.exports = monkeyPatchReactDOM(ReactDOM);
```
### Patching build/react-dom-server.js
- Open `build/react-dom-server.js` and replace all of the document.createElement calls as you did for `build/react-with-addons.js`.
- Change `require('react')` near the top of the file to `require('devtools/client/shared/vendor/react')`.
- About four lines below that require there is a `define(['react'], f);`. Change this to the full path e.g.`define(['devtools/client/shared/vendor/react'], f);`
### Copy the Files Across
- Now we need to copy `react-with-addons.js`, `react-dom.js` and `react-dom-server.js` into our repo (note the destination filenames all have -dev added e.g. `react-dev.js`, these are part of the dev version):
```
cp build/react-with-addons.js
<gecko-dev>/devtools/client/shared/vendor/react-dev.js
cp build/react-dom.js
<gecko-dev>/devtools/client/shared/vendor/react-dom-dev.js
cp build/react-dom-server.js
<gecko-dev>/devtools/client/shared/vendor/react-dom-server-dev.js
```
### Generate a Production Build
```
NODE_ENV=production grunt build
# Or if using the fish shell:
env NODE_ENV=production grunt build
```
### More Patching
Unfortunately, you will need to repeat the following sections **(See note below)**:
- [Patching build/react-with-addons.js](#patching-buildreact-with-addonsjs)
- [Patching build/react-dom.js](#patching-buildreact-domjs)
- [Patching build/react-dom-server.js](#patching-buildreact-dom-serverjs)
**NOTE**: This time you need to save the files with their original filenames so the commands in the "Copy the Files Across" section become:
```
cp build/react-with-addons.js
<gecko-dev>/devtools/client/shared/vendor/react.js
cp build/react-dom.js
<gecko-dev>/devtools/client/shared/vendor/react-dom.js
cp build/react-dom-server.js
<gecko-dev>/devtools/client/shared/vendor/react-dom-server.js
```

11
devtools/client/shared/vendor/moz.build поставляемый
Просмотреть файл

@ -17,10 +17,15 @@ modules += [
'react-addons-shallow-compare.js',
]
# react-dev is used if either debug mode is enabled,
# so include it for both
# react dev versions are used if either debug mode is enabled,
# or enable-debug-js-modules is set in .mozconfig so include it for
# both
if CONFIG['DEBUG_JS_MODULES'] or CONFIG['MOZ_DEBUG']:
modules += ['react-dev.js']
modules += [
'react-dev.js',
'react-dom-dev.js',
'react-dom-server-dev.js',
]
modules += [
'react-dom-server.js',

22155
devtools/client/shared/vendor/react-dev.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

19263
devtools/client/shared/vendor/react-dom-dev.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

16969
devtools/client/shared/vendor/react-dom-server-dev.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

16959
devtools/client/shared/vendor/react-dom-server.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

19189
devtools/client/shared/vendor/react-dom.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

22156
devtools/client/shared/vendor/react.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -601,7 +601,7 @@ html, body, #app, #memory-tool {
svg {
--arrow-color: var(--theme-splitter-color);
--text-color: var(--theme-body-color-alt);
--text-color: var(--theme-body-color);
}
.theme-dark svg {

Просмотреть файл

@ -21,7 +21,7 @@ svg {
overflow: hidden;
-moz-box-flex: 1;
--arrow-color: var(--theme-splitter-color);
--text-color: var(--theme-body-color-alt);
--text-color: var(--theme-body-color);
}
.theme-dark svg {
@ -32,6 +32,7 @@ svg {
.edgePath path {
stroke-width: 1px;
stroke: var(--arrow-color);
fill: none;
}
svg #arrowhead {
/* !important is needed to override inline style */

Просмотреть файл

@ -22,6 +22,7 @@ const { normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_u
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
"@mozilla.org/network/protocol;1?name=resource",
"nsIResProtocolHandler");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
const { defineLazyGetter } = XPCOMUtils;

Просмотреть файл

@ -165,7 +165,6 @@ public:
inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp) const
{
MOZ_ASSERT(!aStamp.IsNull(), "The timestamp should not be null");
if (aStamp.IsNull()) {
return 0;
}

Просмотреть файл

@ -3265,6 +3265,20 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
}
WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
MOZ_ASSERT(wheelEvent);
// When APZ is enabled, the actual scroll animation might be handled by
// the compositor.
WheelPrefs::Action action =
wheelEvent->mFlags.mHandledByAPZ ?
WheelPrefs::ACTION_NONE :
WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent);
// Make the wheel event a horizontal scroll event. I.e., deltaY values
// are set to deltaX and deltaY and deltaZ values are set to 0.
// When AutoWheelDeltaAdjuster instance is destroyed, the delta values
// are restored and make overflow deltaX becomes 0.
AutoWheelDeltaAdjuster adjuster(*wheelEvent);
// Check if the frame to scroll before checking the default action
// because if the scroll target is a plugin, the default action should be
@ -3273,20 +3287,14 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
ComputeScrollTarget(mCurrentTarget, wheelEvent,
COMPUTE_DEFAULT_ACTION_TARGET);
nsPluginFrame* pluginFrame = do_QueryFrame(frameToScroll);
// When APZ is enabled, the actual scroll animation might be handled by
// the compositor.
WheelPrefs::Action action;
if (pluginFrame) {
MOZ_ASSERT(pluginFrame->WantsToHandleWheelEventAsDefaultAction());
action = WheelPrefs::ACTION_SEND_TO_PLUGIN;
} else if (wheelEvent->mFlags.mHandledByAPZ) {
action = WheelPrefs::ACTION_NONE;
} else {
action = WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent);
}
switch (action) {
case WheelPrefs::ACTION_SCROLL: {
case WheelPrefs::ACTION_SCROLL:
case WheelPrefs::ACTION_HORIZONTAL_SCROLL: {
// For scrolling of default action, we should honor the mouse wheel
// transaction.
@ -5657,11 +5665,10 @@ void
EventStateManager::WheelPrefs::Reset()
{
memset(mInit, 0, sizeof(mInit));
}
EventStateManager::WheelPrefs::Index
EventStateManager::WheelPrefs::GetIndexFor(WidgetWheelEvent* aEvent)
EventStateManager::WheelPrefs::GetIndexFor(const WidgetWheelEvent* aEvent)
{
if (!aEvent) {
return INDEX_DEFAULT;
@ -5759,9 +5766,12 @@ EventStateManager::WheelPrefs::Init(EventStateManager::WheelPrefs::Index aIndex)
// Compute action values overridden by .override_x pref.
// At present, override is possible only for the x-direction
// because this pref is introduced mainly for tilt wheels.
// Note that ACTION_HORIZONTAL_SCROLL isn't a valid value for this pref
// because it affects only to deltaY.
prefNameAction.AppendLiteral(".override_x");
int32_t actionOverrideX = Preferences::GetInt(prefNameAction.get(), -1);
if (actionOverrideX < -1 || actionOverrideX > int32_t(ACTION_LAST)) {
if (actionOverrideX < -1 || actionOverrideX > int32_t(ACTION_LAST) ||
actionOverrideX == ACTION_HORIZONTAL_SCROLL) {
NS_WARNING("Unsupported action override pref value, didn't override.");
actionOverrideX = -1;
}
@ -5770,6 +5780,25 @@ EventStateManager::WheelPrefs::Init(EventStateManager::WheelPrefs::Index aIndex)
: static_cast<Action>(actionOverrideX);
}
void
EventStateManager::WheelPrefs::GetMultiplierForDeltaXAndY(
const WidgetWheelEvent* aEvent,
Index aIndex,
double* aMultiplierForDeltaX,
double* aMultiplierForDeltaY)
{
// If the event should be treated as horizontal wheel operation, deltaY
// should be multiplied by mMultiplierY, however, it might be moved to
// deltaX for handling default action. In such case, we need to treat
// mMultiplierX and mMultiplierY as swapped.
*aMultiplierForDeltaX = mMultiplierX[aIndex];
*aMultiplierForDeltaY = mMultiplierY[aIndex];
if (aEvent->mDeltaValuesAdjustedForDefaultHandler &&
ComputeActionFor(aEvent) == ACTION_HORIZONTAL_SCROLL) {
std::swap(*aMultiplierForDeltaX, *aMultiplierForDeltaY);
}
}
void
EventStateManager::WheelPrefs::ApplyUserPrefsToDelta(WidgetWheelEvent* aEvent)
{
@ -5780,16 +5809,19 @@ EventStateManager::WheelPrefs::ApplyUserPrefsToDelta(WidgetWheelEvent* aEvent)
Index index = GetIndexFor(aEvent);
Init(index);
aEvent->mDeltaX *= mMultiplierX[index];
aEvent->mDeltaY *= mMultiplierY[index];
double multiplierForDeltaX = 1.0, multiplierForDeltaY = 1.0;
GetMultiplierForDeltaXAndY(aEvent, index,
&multiplierForDeltaX, &multiplierForDeltaY);
aEvent->mDeltaX *= multiplierForDeltaX;
aEvent->mDeltaY *= multiplierForDeltaY;
aEvent->mDeltaZ *= mMultiplierZ[index];
// If the multiplier is 1.0 or -1.0, i.e., it doesn't change the absolute
// value, we should use lineOrPageDelta values which were set by widget.
// Otherwise, we need to compute them from accumulated delta values.
if (!NeedToComputeLineOrPageDelta(aEvent)) {
aEvent->mLineOrPageDeltaX *= static_cast<int32_t>(mMultiplierX[index]);
aEvent->mLineOrPageDeltaY *= static_cast<int32_t>(mMultiplierY[index]);
aEvent->mLineOrPageDeltaX *= static_cast<int32_t>(multiplierForDeltaX);
aEvent->mLineOrPageDeltaY *= static_cast<int32_t>(multiplierForDeltaY);
} else {
aEvent->mLineOrPageDeltaX = 0;
aEvent->mLineOrPageDeltaY = 0;
@ -5813,16 +5845,19 @@ EventStateManager::WheelPrefs::CancelApplyingUserPrefsFromOverflowDelta(
// happy? Although, widget can know the pref applied delta values by
// referrencing the deltaX and deltaY of the event.
if (mMultiplierX[index]) {
aEvent->mOverflowDeltaX /= mMultiplierX[index];
double multiplierForDeltaX = 1.0, multiplierForDeltaY = 1.0;
GetMultiplierForDeltaXAndY(aEvent, index,
&multiplierForDeltaX, &multiplierForDeltaY);
if (multiplierForDeltaX) {
aEvent->mOverflowDeltaX /= multiplierForDeltaX;
}
if (mMultiplierY[index]) {
aEvent->mOverflowDeltaY /= mMultiplierY[index];
if (multiplierForDeltaY) {
aEvent->mOverflowDeltaY /= multiplierForDeltaY;
}
}
EventStateManager::WheelPrefs::Action
EventStateManager::WheelPrefs::ComputeActionFor(WidgetWheelEvent* aEvent)
EventStateManager::WheelPrefs::ComputeActionFor(const WidgetWheelEvent* aEvent)
{
Index index = GetIndexFor(aEvent);
Init(index);
@ -5831,7 +5866,9 @@ EventStateManager::WheelPrefs::ComputeActionFor(WidgetWheelEvent* aEvent)
(Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaY) &&
Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaZ));
Action* actions = deltaXPreferred ? mOverriddenActionsX : mActions;
if (actions[index] == ACTION_NONE || actions[index] == ACTION_SCROLL) {
if (actions[index] == ACTION_NONE ||
actions[index] == ACTION_SCROLL ||
actions[index] == ACTION_HORIZONTAL_SCROLL) {
return actions[index];
}
@ -5839,8 +5876,11 @@ EventStateManager::WheelPrefs::ComputeActionFor(WidgetWheelEvent* aEvent)
if (aEvent->mIsMomentum) {
// Use the default action. Note that user might kill the wheel scrolling.
Init(INDEX_DEFAULT);
return (actions[INDEX_DEFAULT] == ACTION_SCROLL) ? ACTION_SCROLL :
ACTION_NONE;
if (actions[INDEX_DEFAULT] == ACTION_SCROLL ||
actions[INDEX_DEFAULT] == ACTION_HORIZONTAL_SCROLL) {
return actions[INDEX_DEFAULT];
}
return ACTION_NONE;
}
return actions[index];
@ -5848,7 +5888,7 @@ EventStateManager::WheelPrefs::ComputeActionFor(WidgetWheelEvent* aEvent)
bool
EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
WidgetWheelEvent* aEvent)
const WidgetWheelEvent* aEvent)
{
Index index = GetIndexFor(aEvent);
Init(index);
@ -5858,15 +5898,19 @@ EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
}
void
EventStateManager::WheelPrefs::GetUserPrefsForEvent(WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY)
EventStateManager::WheelPrefs::GetUserPrefsForEvent(
const WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY)
{
Index index = GetIndexFor(aEvent);
Init(index);
*aOutMultiplierX = mMultiplierX[index];
*aOutMultiplierY = mMultiplierY[index];
double multiplierForDeltaX = 1.0, multiplierForDeltaY = 1.0;
GetMultiplierForDeltaXAndY(aEvent, index,
&multiplierForDeltaX, &multiplierForDeltaY);
*aOutMultiplierX = multiplierForDeltaX;
*aOutMultiplierY = multiplierForDeltaY;
}
// static
@ -5879,15 +5923,34 @@ EventStateManager::WheelPrefs::WheelEventsEnabledOnPlugins()
return sWheelEventsEnabledOnPlugins;
}
// static
bool
EventStateManager::WheelEventIsScrollAction(WidgetWheelEvent* aEvent)
EventStateManager::WheelEventIsScrollAction(const WidgetWheelEvent* aEvent)
{
return aEvent->mMessage == eWheel &&
WheelPrefs::GetInstance()->ComputeActionFor(aEvent) == WheelPrefs::ACTION_SCROLL;
if (aEvent->mMessage != eWheel) {
return false;
}
WheelPrefs::Action action =
WheelPrefs::GetInstance()->ComputeActionFor(aEvent);
return action == WheelPrefs::ACTION_SCROLL ||
action == WheelPrefs::ACTION_HORIZONTAL_SCROLL;
}
// static
bool
EventStateManager::WheelEventIsHorizontalScrollAction(
const WidgetWheelEvent* aEvent)
{
if (aEvent->mMessage != eWheel) {
return false;
}
WheelPrefs::Action action =
WheelPrefs::GetInstance()->ComputeActionFor(aEvent);
return action == WheelPrefs::ACTION_HORIZONTAL_SCROLL;
}
void
EventStateManager::GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent,
EventStateManager::GetUserPrefsForWheelEvent(const WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY)
{
@ -5897,7 +5960,7 @@ EventStateManager::GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent,
bool
EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedX(
WidgetWheelEvent* aEvent)
const WidgetWheelEvent* aEvent)
{
Index index = GetIndexFor(aEvent);
Init(index);
@ -5907,7 +5970,7 @@ EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedX(
bool
EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY(
WidgetWheelEvent* aEvent)
const WidgetWheelEvent* aEvent)
{
Index index = GetIndexFor(aEvent);
Init(index);

Просмотреть файл

@ -304,10 +304,14 @@ public:
static bool IsRemoteTarget(nsIContent* aTarget);
// Returns true if the given WidgetWheelEvent will resolve to a scroll action.
static bool WheelEventIsScrollAction(WidgetWheelEvent* aEvent);
static bool WheelEventIsScrollAction(const WidgetWheelEvent* aEvent);
// Returns true if the given WidgetWheelEvent will resolve to a horizontal
// scroll action but it's a vertical wheel operation.
static bool WheelEventIsHorizontalScrollAction(const WidgetWheelEvent* aEvet);
// Returns user-set multipliers for a wheel event.
static void GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent,
static void GetUserPrefsForWheelEvent(const WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY);
@ -538,7 +542,7 @@ protected:
* Returns whether or not ApplyUserPrefsToDelta() would change the delta
* values of an event.
*/
void GetUserPrefsForEvent(WidgetWheelEvent* aEvent,
void GetUserPrefsForEvent(const WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY);
@ -558,25 +562,26 @@ protected:
ACTION_SCROLL,
ACTION_HISTORY,
ACTION_ZOOM,
ACTION_LAST = ACTION_ZOOM,
ACTION_HORIZONTAL_SCROLL,
ACTION_LAST = ACTION_HORIZONTAL_SCROLL,
// Following actions are used only by internal processing. So, cannot
// specified by prefs.
ACTION_SEND_TO_PLUGIN
ACTION_SEND_TO_PLUGIN,
};
Action ComputeActionFor(WidgetWheelEvent* aEvent);
Action ComputeActionFor(const WidgetWheelEvent* aEvent);
/**
* NeedToComputeLineOrPageDelta() returns if the aEvent needs to be
* computed the lineOrPageDelta values.
*/
bool NeedToComputeLineOrPageDelta(WidgetWheelEvent* aEvent);
bool NeedToComputeLineOrPageDelta(const WidgetWheelEvent* aEvent);
/**
* IsOverOnePageScrollAllowed*() checks whether wheel scroll amount should
* be rounded down to the page width/height (false) or not (true).
*/
bool IsOverOnePageScrollAllowedX(WidgetWheelEvent* aEvent);
bool IsOverOnePageScrollAllowedY(WidgetWheelEvent* aEvent);
bool IsOverOnePageScrollAllowedX(const WidgetWheelEvent* aEvent);
bool IsOverOnePageScrollAllowedY(const WidgetWheelEvent* aEvent);
/**
* WheelEventsEnabledOnPlugins() returns true if user wants to use mouse
@ -609,7 +614,7 @@ protected:
* default index which is used at either no modifier key is pressed or
* two or modifier keys are pressed.
*/
Index GetIndexFor(WidgetWheelEvent* aEvent);
Index GetIndexFor(const WidgetWheelEvent* aEvent);
/**
* GetPrefNameBase() returns the base pref name for aEvent.
@ -625,6 +630,25 @@ protected:
void Reset();
/**
* Retrieve multiplier for aEvent->mDeltaX and aEvent->mDeltaY.
* If the default action is ACTION_HORIZONTAL_SCROLL and the delta values
* are adjusted by AutoWheelDeltaAdjuster(), this treats mMultiplierX as
* multiplier for deltaY and mMultiplierY as multiplier for deltaY.
*
* @param aEvent The event which is being handled.
* @param aIndex The index of mMultiplierX and mMultiplierY.
* Should be result of GetIndexFor(aEvent).
* @param aMultiplierForDeltaX Will be set to multiplier for
* aEvent->mDeltaX.
* @param aMultiplierForDeltaY Will be set to multiplier for
* aEvent->mDeltaY.
*/
void GetMultiplierForDeltaXAndY(const WidgetWheelEvent* aEvent,
Index aIndex,
double* aMultiplierForDeltaX,
double* aMultiplierForDeltaY);
bool mInit[COUNT_OF_MULTIPLIERS];
double mMultiplierX[COUNT_OF_MULTIPLIERS];
double mMultiplierY[COUNT_OF_MULTIPLIERS];

Просмотреть файл

@ -529,8 +529,9 @@ ScrollbarsForWheel::DeactivateAllTemporarilyActivatedScrollTargets()
}
/******************************************************************/
/* mozilla::WheelTransaction */
/* mozilla::WheelTransaction::Prefs */
/******************************************************************/
int32_t WheelTransaction::Prefs::sMouseWheelAccelerationStart = -1;
int32_t WheelTransaction::Prefs::sMouseWheelAccelerationFactor = -1;
uint32_t WheelTransaction::Prefs::sMouseWheelTransactionTimeout = 1500;
@ -555,4 +556,47 @@ WheelTransaction::Prefs::InitializeStatics()
}
}
/******************************************************************/
/* mozilla::AutoWheelDeltaAdjuster */
/******************************************************************/
AutoWheelDeltaAdjuster::AutoWheelDeltaAdjuster(WidgetWheelEvent& aWheelEvent)
: mWheelEvent(aWheelEvent)
, mOldDeltaX(aWheelEvent.mDeltaX)
, mOldDeltaZ(aWheelEvent.mDeltaZ)
, mOldOverflowDeltaX(aWheelEvent.mOverflowDeltaX)
, mOldLineOrPageDeltaX(aWheelEvent.mLineOrPageDeltaX)
, mTreatedVerticalWheelAsHorizontalScroll(false)
{
MOZ_ASSERT(!aWheelEvent.mDeltaValuesAdjustedForDefaultHandler);
if (EventStateManager::WheelEventIsHorizontalScrollAction(&aWheelEvent)) {
// Move deltaY values to deltaX and set both deltaY and deltaZ to 0.
mWheelEvent.mDeltaX = mWheelEvent.mDeltaY;
mWheelEvent.mDeltaY = 0.0;
mWheelEvent.mDeltaZ = 0.0;
mWheelEvent.mOverflowDeltaX = mWheelEvent.mOverflowDeltaY;
mWheelEvent.mOverflowDeltaY = 0.0;
mWheelEvent.mLineOrPageDeltaX = mWheelEvent.mLineOrPageDeltaY;
mWheelEvent.mLineOrPageDeltaY = 0;
mWheelEvent.mDeltaValuesAdjustedForDefaultHandler = true;
mTreatedVerticalWheelAsHorizontalScroll = true;
}
}
AutoWheelDeltaAdjuster::~AutoWheelDeltaAdjuster()
{
if (mTreatedVerticalWheelAsHorizontalScroll &&
mWheelEvent.mDeltaValuesAdjustedForDefaultHandler) {
mWheelEvent.mDeltaY = mWheelEvent.mDeltaX;
mWheelEvent.mDeltaX = mOldDeltaX;
mWheelEvent.mDeltaZ = mOldDeltaZ;
mWheelEvent.mOverflowDeltaY = mWheelEvent.mOverflowDeltaX;
mWheelEvent.mOverflowDeltaX = mOldOverflowDeltaX;
mWheelEvent.mLineOrPageDeltaY = mWheelEvent.mLineOrPageDeltaX;
mWheelEvent.mLineOrPageDeltaX = mOldLineOrPageDeltaX;
mWheelEvent.mDeltaValuesAdjustedForDefaultHandler = false;
}
}
} // namespace mozilla

Просмотреть файл

@ -204,6 +204,35 @@ protected:
};
};
/**
* When a wheel event should be treated as specially, e.g., it's a vertical
* wheel operation but user wants to scroll the target horizontally, this
* class adjust the delta values automatically. Then, restores the original
* value when the instance is destroyed.
*/
class MOZ_STACK_CLASS AutoWheelDeltaAdjuster final
{
public:
/**
* @param aWheelEvent A wheel event. The delta values may be
* modified for default handler.
* Its mDeltaValuesAdjustedForDefaultHandler
* must not be true because if it's true,
* the event has already been adjusted the
* delta values for default handler.
*/
explicit AutoWheelDeltaAdjuster(WidgetWheelEvent& aWheelEvent);
~AutoWheelDeltaAdjuster();
private:
WidgetWheelEvent& mWheelEvent;
double mOldDeltaX;
double mOldDeltaZ;
double mOldOverflowDeltaX;
int32_t mOldLineOrPageDeltaX;
bool mTreatedVerticalWheelAsHorizontalScroll;
};
} // namespace mozilla
#endif // mozilla_WheelHandlingHelper_h_

Просмотреть файл

@ -36,6 +36,7 @@ EXPORTS.mozilla += [
'PhysicalKeyCodeNameList.h',
'TextComposition.h',
'VirtualKeyCodeList.h',
'WheelHandlingHelper.h',
]
EXPORTS.mozilla.dom += [

Просмотреть файл

@ -78,6 +78,18 @@ var gScrollableElement = document.getElementById("scrollable");
var gScrolledElement = document.getElementById("scrolled");
var gSpacerForBodyElement = document.getElementById("spacerForBody");
const kDefaultActionNone = 0;
const kDefaultActionScroll = 1;
const kDefaultActionHistory = 2;
const kDefaultActionZoom = 3;
const kDefaultActionHorizontalScroll = 4;
const kDefaultActionOverrideXNoOverride = -1;
const kDefaultActionOverrideXNone = kDefaultActionNone;
const kDefaultActionOverrideXScroll = kDefaultActionScroll;
const kDefaultActionOverrideXHistory = kDefaultActionHistory;
const kDefaultActionOverrideXZoom = kDefaultActionZoom;
function is()
{
window.opener.is.apply(window.opener, arguments);
@ -538,98 +550,98 @@ function doTestScroll(aSettings, aCallback)
deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollDown },
{ description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 1, even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollDown },
{ description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollUp },
{ description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is -1, even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollUp },
{ description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 1, even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is -1, even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll to bottom-right by momentum pixel scroll even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollDown | kScrollRight },
{ description: "Scroll to bottom-left by momentum pixel scroll even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollDown | kScrollLeft },
{ description: "Scroll to top-left by momentum pixel scroll even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollUp | kScrollLeft },
{ description: "Scroll to top-right by momentum pixel scroll even if the action is history",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kScrollUp | kScrollRight },
{ description: "Not Scroll by momentum pixel scroll for z (action is history)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Not Scroll by momentum pixel scroll if default action is none (action is history)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, osKey: false },
expected: kNoScroll,
prepare: function (cb) { SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.action", 0]]}, cb); },
cleanup: function (cb) { SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.action", 1]]}, cb); } },
@ -946,6 +958,350 @@ function doTestScroll(aSettings, aCallback)
doNextTest();
}
function doTestHorizontalScroll(aSettings, aCallback)
{
const kNoScroll = 0x00;
const kScrollLeft = 0x01;
const kScrollRight = 0x02;
const kTests = [
{ description: "Scroll to right by pixel scroll even if lineOrPageDelta is 0",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to right by pixel scroll when lineOrPageDelta is 1",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to left by pixel scroll even if lineOrPageDelta is 0",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll to left by pixel scroll when lineOrPageDelta is -1",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Don't scroll by deltaX (pixel scroll, lineOrPageDelta is 0)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by deltaX (pixel scroll, lineOrPageDelta is 1)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by negative deltaX (pixel scroll, lineOrPageDelta is 0)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by negative deltaX (pixel scroll, lineOrPageDelta is -1)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Scroll only to right by diagonal pixel scroll (to bottom-right)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll only to right by diagonal pixel scroll (to bottom-left)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll only to left by diagonal pixel scroll (to top-left)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll only to left by pixel scroll (to bottom-right)",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Don't scroll by pixel scroll for z-axis",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Scroll to right by line scroll even if lineOrPageDelta is 0",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to right by line scroll when lineOrPageDelta is 1",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to left by line scroll even if lineOrPageDelta is 0",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll to left by line scroll when lineOrPageDelta is -1",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Don't scroll by deltaX (line scroll, lineOrPageDelta is 0)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by deltaX (line scroll, lineOrPageDelta is 1)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by negative deltaX (line scroll, lineOrPageDelta is 0)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by negative deltaY (line scroll, lineOrPageDelta is -1)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Scroll only to right by diagonal line scroll (to bottom-right)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll only to right by diagonal line scroll (to bottom-left)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll only to left by diagonal line scroll (to top-left)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll only to left by line scroll (to top-right)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Don't scroll by line scroll for z-axis",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Scroll to right by page scroll even if lineOrPageDelta is 0",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to right by page scroll when lineOrPageDelta is 1",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll to left by page scroll even if lineOrPageDelta is 0",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll to left by page scroll when lineOrPageDelta is -1",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is 0)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is 1)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is 0)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is -1)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
{ description: "Scroll only to right by diagonal page scroll (to bottom-right)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll only to right by diagonal page scroll (to bottom-left)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollRight },
{ description: "Scroll only to left by diagonal page scroll (to top-left)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Scroll only to left by diagonal page scroll (to top-right)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kScrollLeft },
{ description: "Don't scroll by page scroll for z-axis",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
expected: kNoScroll },
];
var description;
var currentTestIndex = -1;
// deltaY should cause horizontal scroll and affected by deltaMultiplierY.
// So, horizontal scroll amount and direction is affected by deltaMultiplierY.
var isXReverted = (aSettings.deltaMultiplierY < 0);
function doNextTest()
{
if (++currentTestIndex >= kTests.length) {
SimpleTest.executeSoon(aCallback);
return;
}
gScrollableElement.scrollTop = 1000;
gScrollableElement.scrollLeft = 1000;
var currentTest = kTests[currentTestIndex];
description = "doTestHorizontalScroll(aSettings=" + aSettings.description + "), " + currentTest.description + ": ";
if (currentTest.prepare) {
currentTest.prepare(doTestCurrentScroll);
} else {
doTestCurrentScroll();
}
}
function doTestCurrentScroll() {
var currentTest = kTests[currentTestIndex];
sendWheelAndWait(10, 10, currentTest.event, function () {
is(gScrollableElement.scrollTop, 1000, description + "scrolled vertical");
if (currentTest.expected == kNoScroll) {
is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
} else {
var scrollLeft = !isXReverted ? (currentTest.expected & kScrollLeft) :
(currentTest.expected & kScrollRight);
var scrollRight = !isXReverted ? (currentTest.expected & kScrollRight) :
(currentTest.expected & kScrollLeft);
if (scrollLeft) {
ok(gScrollableElement.scrollLeft < 1000, description + "not scrolled to left, got " + gScrollableElement.scrollLeft);
} else if (scrollRight) {
ok(gScrollableElement.scrollLeft > 1000, description + "not scrolled to right, got " + gScrollableElement.scrollLeft);
} else {
is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
}
}
if (currentTest.cleanup) {
currentTest.cleanup(nextStep);
} else {
nextStep();
}
function nextStep() {
winUtils.advanceTimeAndRefresh(100);
doNextTest();
}
});
}
doNextTest();
}
function doTestZoom(aSettings, aCallback)
{
if ((aSettings.deltaMultiplierX != 1.0 && aSettings.deltaMultiplierX != -1.0) ||
@ -1659,109 +2015,109 @@ function doTestActionOverride(aCallback)
const kScrollRight = 0x08;
const kTests = [
{ action: 1, override_x: -1,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNoOverride,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 1, override_x: 0,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNone,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 1, override_x: 1,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXScroll,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 0, override_x: -1,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXNoOverride,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 0, override_x: 0,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXNone,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 0, override_x: 1,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXScroll,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 1, override_x: -1,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNoOverride,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 0.5,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 1, override_x: 0,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNone,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 0.5,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 1, override_x: 1,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXScroll,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 0.5,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 0, override_x: -1,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXNoOverride,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 0.5,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 0, override_x: 0,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXNone,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 0.5,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 0, override_x: 1,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXScroll,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 0.5,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 1, override_x: -1,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNoOverride,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 1, override_x: 0,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNone,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 1, override_x: 1,
{ action: kDefaultActionScroll, override_x: kDefaultActionOverrideXScroll,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kScrollDown | kScrollRight
},
{ action: 0, override_x: -1,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXNoOverride,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 0, override_x: 0,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXNone,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
expected: kNoScroll
},
{ action: 0, override_x: 1,
{ action: kDefaultActionNone, override_x: kDefaultActionOverrideXScroll,
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0.5, deltaY: 1.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
@ -1820,12 +2176,14 @@ function runTests()
SpecialPowers.pushPrefEnv({"set": [
["test.events.async.enabled", true],
["general.smoothScroll", false],
["mousewheel.default.action", 1], // scroll
["mousewheel.default.action.override_x", -1],
["mousewheel.with_shift.action", 2], // history
["mousewheel.with_shift.action.override_x", -1],
["mousewheel.with_control.action", 3], // zoom
["mousewheel.with_control.action.override_x", -1]]},
["mousewheel.default.action", kDefaultActionScroll],
["mousewheel.default.action.override_x", kDefaultActionOverrideXNoOverride],
["mousewheel.with_shift.action", kDefaultActionHorizontalScroll],
["mousewheel.with_shift.action.override_x", kDefaultActionOverrideXNoOverride],
["mousewheel.with_control.action", kDefaultActionZoom],
["mousewheel.with_control.action.override_x", kDefaultActionOverrideXNoOverride],
["mousewheel.with_alt.action", kDefaultActionHistory],
["mousewheel.with_alt.action.override_x", kDefaultActionOverrideXNoOverride]]},
runTests2);
}
@ -1859,20 +2217,22 @@ function runTests2()
function doTest() {
setDeltaMultiplierSettings(kSettings[index], function () {
doTestScroll(kSettings[index], function () {
doTestZoom(kSettings[index], function() {
if (++index == kSettings.length) {
setDeltaMultiplierSettings(kSettings[0], function() {
doTestZoomedScroll(function() {
doTestWholeScroll(function() {
doTestActionOverride(function() {
finishTests();
doTestHorizontalScroll(kSettings[index], function() {
doTestZoom(kSettings[index], function() {
if (++index == kSettings.length) {
setDeltaMultiplierSettings(kSettings[0], function() {
doTestZoomedScroll(function() {
doTestWholeScroll(function() {
doTestActionOverride(function() {
finishTests();
});
});
});
});
});
} else {
doTest();
}
} else {
doTest();
}
});
});
});
});

Просмотреть файл

@ -2280,7 +2280,7 @@ void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
NotifyOwnerDocumentActivityChanged();
}
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
UpdateReadyStateInternal();
}
void HTMLMediaElement::DealWithFailedElement(nsIContent* aSourceElement)
@ -3972,13 +3972,11 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
: nsGenericHTMLElement(aNodeInfo),
mMainThreadEventTarget(OwnerDoc()->EventTargetFor(TaskCategory::Other)),
mAbstractMainThread(OwnerDoc()->AbstractMainThreadFor(TaskCategory::Other)),
mWatchManager(this, mAbstractMainThread),
mSrcStreamTracksAvailable(false),
mSrcStreamPausedCurrentTime(-1),
mShutdownObserver(new ShutdownObserver),
mSourcePointer(nullptr),
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING, "HTMLMediaElement::mReadyState"),
mCurrentLoadID(0),
mLoadWaitStatus(NOT_WAITING),
mVolume(1.0),
@ -4021,7 +4019,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mCORSMode(CORS_NONE),
mIsEncrypted(false),
mWaitingForKey(NOT_WAITING_FOR_KEY),
mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
mDisableVideo(false),
mHasUserInteraction(false),
mFirstFrameLoaded(false),
@ -4043,12 +4040,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
RegisterActivityObserver();
NotifyOwnerDocumentActivityChanged();
MOZ_ASSERT(NS_IsMainThread());
mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal);
// Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses
// to run until mReadyState reaches at least HAVE_METADATA by some other means.
mWatchManager.Watch(mReadyState, &HTMLMediaElement::UpdateReadyStateInternal);
mShutdownObserver->Subscribe(this);
nsIDocShell* docShell = OwnerDoc()->GetDocShell();
if (docShell) {
@ -4885,7 +4876,7 @@ HTMLMediaElement::AssertReadyStateIsNothing()
"mSourceLoadCandidate=%d "
"mIsLoadingFromSourceChildren=%d mPreloadAction=%d "
"mSuspendedForPreloadNone=%d error=%d",
int(mReadyState.Ref()),
int(mReadyState),
int(mNetworkState),
int(mLoadWaitStatus),
!!mSourceLoadCandidate,
@ -5090,12 +5081,10 @@ HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder)
return rv;
}
class HTMLMediaElement::StreamListener : public MediaStreamListener,
public WatchTarget
class HTMLMediaElement::StreamListener : public MediaStreamListener
{
public:
StreamListener(HTMLMediaElement* aElement, const char* aName) :
WatchTarget(aName),
mElement(aElement),
mHaveCurrentData(false),
mFinished(false),
@ -5104,8 +5093,11 @@ public:
{}
void Forget()
{
mElement = nullptr;
NotifyWatchers();
if (mElement) {
HTMLMediaElement* element = mElement;
mElement = nullptr;
element->UpdateReadyStateInternal();
}
}
// Main thread
@ -5135,8 +5127,8 @@ public:
if (mElement) {
RefPtr<HTMLMediaElement> kungFuDeathGrip = mElement;
kungFuDeathGrip->FirstFrameLoaded();
kungFuDeathGrip->UpdateReadyStateInternal();
}
NotifyWatchers();
DoNotifyOutput();
}
@ -5259,9 +5251,6 @@ void HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags)
"HTMLMediaElement::mMediaStreamListener");
stream->AddListener(mMediaStreamListener);
mWatchManager.Watch(*mMediaStreamListener,
&HTMLMediaElement::UpdateReadyStateInternal);
stream->AddAudioOutput(this);
SetVolumeInternal();
@ -5291,9 +5280,6 @@ void HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags)
// If stream is null, then DOMMediaStream::Destroy must have been
// called and that will remove all listeners/outputs.
mWatchManager.Unwatch(*mMediaStreamListener,
&HTMLMediaElement::UpdateReadyStateInternal);
mMediaStreamListener->Forget();
mMediaStreamListener = nullptr;
}
@ -5430,7 +5416,7 @@ HTMLMediaElement::NotifyMediaStreamTrackAdded(const RefPtr<MediaStreamTrack>& aT
}
}
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
UpdateReadyStateInternal();
}
void
@ -5514,8 +5500,6 @@ HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
mPendingEncryptedInitData.Reset();
}
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
if (IsVideo() && aInfo->HasVideo()) {
// We are a video element playing video so update the screen wakelock
NotifyOwnerDocumentActivityChanged();
@ -5526,6 +5510,8 @@ HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
mDefaultPlaybackStartPosition = 0.0;
}
UpdateReadyStateInternal();
if (!mSrcStream) {
return;
}
@ -5701,6 +5687,7 @@ void
HTMLMediaElement::NotifySuspendedByCache(bool aSuspendedByCache)
{
mDownloadSuspendedByCache = aSuspendedByCache;
UpdateReadyStateInternal();
}
void HTMLMediaElement::DownloadSuspended()
@ -6035,17 +6022,21 @@ static const char* const gReadyStateToString[] = {
"HAVE_ENOUGH_DATA"
};
void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
void
HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
{
nsMediaReadyState oldState = mReadyState;
mReadyState = aState;
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY ||
oldState == mReadyState) {
if (mReadyState == aState) {
return;
}
LOG(LogLevel::Debug, ("%p Ready state changed to %s", this, gReadyStateToString[aState]));
nsMediaReadyState oldState = mReadyState;
mReadyState = aState;
LOG(LogLevel::Debug,
("%p Ready state changed to %s", this, gReadyStateToString[aState]));
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
return;
}
UpdateAudioChannelPlayingState();
@ -6439,7 +6430,7 @@ void HTMLMediaElement::UpdateMediaSize(const nsIntSize& aSize)
}
mMediaInfo.mVideo.mDisplay = aSize;
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
UpdateReadyStateInternal();
}
void HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize)

Просмотреть файл

@ -1352,9 +1352,6 @@ protected:
// Used by streams captured from this element.
nsTArray<DecoderPrincipalChangeObserver*> mDecoderPrincipalChangeObservers;
// State-watching manager.
WatchManager<HTMLMediaElement> mWatchManager;
// A reference to the VideoFrameContainer which contains the current frame
// of video to display.
RefPtr<VideoFrameContainer> mVideoFrameContainer;
@ -1430,7 +1427,7 @@ protected:
// Media loading flags. See:
// http://www.whatwg.org/specs/web-apps/current-work/#video)
nsMediaNetworkState mNetworkState;
Watchable<nsMediaReadyState> mReadyState;
nsMediaReadyState mReadyState = nsIDOMHTMLMediaElement::HAVE_NOTHING;
enum LoadAlgorithmState {
// No load algorithm instance is waiting for a source to be added to the
@ -1683,7 +1680,7 @@ protected:
EncryptionInfo mPendingEncryptedInitData;
// True if the media's channel's download has been suspended.
Watchable<bool> mDownloadSuspendedByCache;
bool mDownloadSuspendedByCache = false;
// Disable the video playback by track selection. This flag might not be
// enough if we ever expand the ability of supporting multi-tracks video

Просмотреть файл

@ -127,7 +127,7 @@ GetPluginFile(const nsAString& aPluginPath,
return GetPluginFile(aPluginPath, unusedlibDir, aLibFile);
}
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
#if defined(XP_MACOSX)
static nsCString
GetNativeTarget(nsIFile* aFile)
{
@ -142,6 +142,7 @@ GetNativeTarget(nsIFile* aFile)
return path;
}
#if defined(MOZ_GMP_SANDBOX)
static bool
GetPluginPaths(const nsAString& aPluginPath,
nsCString &aPluginDirectoryPath,
@ -230,7 +231,8 @@ GMPChild::SetMacSandboxInfo(MacSandboxPluginType aPluginType)
mGMPLoader->SetSandboxInfo(&info);
return true;
}
#endif // XP_MACOSX && MOZ_GMP_SANDBOX
#endif // MOZ_GMP_SANDBOX
#endif // XP_MACOSX
bool
GMPChild::Init(const nsAString& aPluginPath,

Просмотреть файл

@ -9,6 +9,7 @@
#include "MediaSourceUtils.h"
#include "OpusDecoder.h"
#include "SourceBufferList.h"
#include "VorbisDecoder.h"
#include "nsPrintfCString.h"
#include <algorithm>
@ -307,7 +308,8 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
, mManager(aManager)
, mReset(true)
, mPreRoll(TimeUnit::FromMicroseconds(
OpusDataDecoder::IsOpus(mParent->GetTrackInfo(mType)->mMimeType)
OpusDataDecoder::IsOpus(mParent->GetTrackInfo(mType)->mMimeType) ||
VorbisDataDecoder::IsVorbis(mParent->GetTrackInfo(mType)->mMimeType)
? 80000
: mParent->GetTrackInfo(mType)->mMimeType.EqualsLiteral("audio/mp4a-latm")
// AAC encoder delay is by default 2112 audio frames.

Просмотреть файл

@ -156,8 +156,6 @@ VorbisDataDecoder::ProcessDecode(MediaRawData* aSample)
const unsigned char* aData = aSample->Data();
size_t aLength = aSample->Size();
int64_t aOffset = aSample->mOffset;
auto aTstampUsecs = aSample->mTime;
int64_t aTotalFrames = 0;
MOZ_ASSERT(mPacketCount >= 3);
@ -225,12 +223,12 @@ VorbisDataDecoder::ProcessDecode(MediaRawData* aSample)
__func__);
}
auto time = total_duration + aTstampUsecs;
auto time = total_duration + aSample->mTime;
if (!time.IsValid()) {
return DecodePromise::CreateAndReject(
MediaResult(
NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
RESULT_DETAIL("Overflow adding total_duration and aTstampUsecs")),
RESULT_DETAIL("Overflow adding total_duration and aSample->mTime")),
__func__);
};
@ -250,8 +248,6 @@ VorbisDataDecoder::ProcessDecode(MediaRawData* aSample)
AudioSampleBuffer data(Move(buffer));
data = mAudioConverter->Process(Move(data));
aTotalFrames += frames;
results.AppendElement(new AudioData(aOffset, time, duration,
frames, data.Forget(), channels, rate));
mFrames += frames;

Просмотреть файл

@ -129,7 +129,6 @@ protected:
static StringInfo sStringInfo[2];
nsCOMPtr<nsIContent> mOriginal; // if we've been cloned, our "real" copy
nsCOMPtr<nsIContent> mClone; // cloned tree
ElementTracker mReferencedElementTracker;
RefPtr<URLExtraData> mContentURLData; // URL data for its anonymous content
};

Просмотреть файл

@ -41,7 +41,9 @@
#include <windows.h>
#include <accctrl.h>
#define PATH_MAX MAX_PATH
#ifndef PATH_MAX
# define PATH_MAX MAX_PATH
#endif
#endif // defined(XP_WIN)

Просмотреть файл

@ -114,6 +114,7 @@ support-files = test_offsets.js
[test_paste_selection.html]
[test_performance_now.html]
[test_performance_timeline.html]
[test_performance_nav_timing_before_onload.html]
[test_picture_apng.html]
[test_picture_mutations.html]
[test_pointerPreserves3D.html]

Просмотреть файл

@ -0,0 +1,30 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for Bug 1405961 - Using PerformanceNavigationTiming before onload</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="content"> </div>
<script>
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_navigation_timing", true]]}, start);
function start() {
var p = performance.getEntriesByName("document")[0];
ok(!!p, "There should be an entry for the document");
document.getElementById("content").textContent += JSON.stringify(p);
SimpleTest.finish();
}
</script>
</body>
</html>

Просмотреть файл

@ -36,4 +36,6 @@ interface WebrtcGlobalInformation {
// WebRTC AEC debugging enable
static attribute boolean aecDebug;
static readonly attribute DOMString aecDebugLogDir;
};

Просмотреть файл

@ -175,4 +175,4 @@ Troubleshooting tips:
-------------------------------------------------------------------------------
The version of WebRender currently in the tree is:
a624aa6d3b6006c510c8b14026567af4ac545d2f
7892f5364bc4d35c7a9b42949f0ace4cc54f8b3c

Просмотреть файл

@ -13,6 +13,7 @@
#include "mozilla/MouseEvents.h" // for WidgetMouseEvent
#include "mozilla/TextEvents.h" // for WidgetKeyboardEvent
#include "mozilla/TouchEvents.h" // for WidgetTouchEvent
#include "mozilla/WheelHandlingHelper.h" // for AutoWheelDeltaAdjuster
namespace mozilla {
namespace layers {
@ -113,31 +114,39 @@ IAPZCTreeManager::ReceiveInputEvent(
scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
}
ScreenPoint origin(wheelEvent.mRefPoint.x, wheelEvent.mRefPoint.y);
ScrollWheelInput input(wheelEvent.mTime, wheelEvent.mTimeStamp, 0,
scrollMode,
ScrollWheelInput::DeltaTypeForDeltaMode(
wheelEvent.mDeltaMode),
origin,
wheelEvent.mDeltaX, wheelEvent.mDeltaY,
wheelEvent.mAllowToOverrideSystemScrollSpeed);
// AutoWheelDeltaAdjuster may adjust the delta values for default
// action hander. The delta values will be restored automatically
// when its instance is destroyed.
AutoWheelDeltaAdjuster adjuster(wheelEvent);
// We add the user multiplier as a separate field, rather than premultiplying
// it, because if the input is converted back to a WidgetWheelEvent, then
// EventStateManager would apply the delta a second time. We could in theory
// work around this by asking ESM to customize the event much sooner, and
// then save the "mCustomizedByUserPrefs" bit on ScrollWheelInput - but for
// now, this seems easier.
EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
&input.mUserDeltaMultiplierX,
&input.mUserDeltaMultiplierY);
// If the wheel event becomes no-op event, don't handle it as scroll.
if (wheelEvent.mDeltaX || wheelEvent.mDeltaY) {
ScreenPoint origin(wheelEvent.mRefPoint.x, wheelEvent.mRefPoint.y);
ScrollWheelInput input(wheelEvent.mTime, wheelEvent.mTimeStamp, 0,
scrollMode,
ScrollWheelInput::DeltaTypeForDeltaMode(
wheelEvent.mDeltaMode),
origin,
wheelEvent.mDeltaX, wheelEvent.mDeltaY,
wheelEvent.mAllowToOverrideSystemScrollSpeed);
nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
wheelEvent.mRefPoint.x = input.mOrigin.x;
wheelEvent.mRefPoint.y = input.mOrigin.y;
wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
wheelEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
return status;
// We add the user multiplier as a separate field, rather than premultiplying
// it, because if the input is converted back to a WidgetWheelEvent, then
// EventStateManager would apply the delta a second time. We could in theory
// work around this by asking ESM to customize the event much sooner, and
// then save the "mCustomizedByUserPrefs" bit on ScrollWheelInput - but for
// now, this seems easier.
EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
&input.mUserDeltaMultiplierX,
&input.mUserDeltaMultiplierY);
nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
wheelEvent.mRefPoint.x = input.mOrigin.x;
wheelEvent.mRefPoint.y = input.mOrigin.y;
wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
wheelEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
return status;
}
}
UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);

Просмотреть файл

@ -11,6 +11,7 @@ default = ["freetype-lib"]
freetype-lib = ["freetype/servo-freetype-sys"]
profiler = ["thread_profiler/thread_profiler"]
debugger = ["ws", "serde_json", "serde", "serde_derive"]
query = []
[dependencies]
app_units = "0.5.6"
@ -25,7 +26,7 @@ num-traits = "0.1.32"
time = "0.1"
rayon = "0.8"
webrender_api = {path = "../webrender_api"}
bitflags = "0.9"
bitflags = "1.0"
thread_profiler = "0.1.1"
plane-split = "0.6"
ws = { optional = true, version = "0.7.3" }

Просмотреть файл

@ -187,7 +187,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::P),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::PROFILER_DBG);
flags.toggle(webrender::DebugFlags::PROFILER_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(
@ -196,7 +196,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::O),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::RENDER_TARGET_DBG);
flags.toggle(webrender::DebugFlags::RENDER_TARGET_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(
@ -205,7 +205,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::I),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::TEXTURE_CACHE_DBG);
flags.toggle(webrender::DebugFlags::TEXTURE_CACHE_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(
@ -214,7 +214,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::B),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::ALPHA_PRIM_DBG);
flags.toggle(webrender::DebugFlags::ALPHA_PRIM_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(

Просмотреть файл

@ -0,0 +1,81 @@
/* 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/. */
varying vec2 vLocalPos;
flat varying vec4 vLocalRect;
#ifdef WR_VERTEX_SHADER
struct BrushInstance {
int picture_address;
int prim_address;
};
BrushInstance load_brush() {
BrushInstance bi;
bi.picture_address = aData0.x;
bi.prim_address = aData0.y;
return bi;
}
/*
The dynamic picture that this brush exists on. Right now, it
contains minimal information. In the future, it will describe
the transform mode of primitives on this picture, among other things.
*/
struct PictureTask {
RectWithSize target_rect;
};
PictureTask fetch_picture_task(int index) {
ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
vec4 target_rect = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
PictureTask task = PictureTask(RectWithSize(target_rect.xy, target_rect.zw));
return task;
}
void main(void) {
// Load the brush instance from vertex attributes.
BrushInstance brush = load_brush();
// Fetch the dynamic picture that we are drawing on.
PictureTask pic_task = fetch_picture_task(brush.picture_address);
// Load the geometry for this brush. For now, this is simply the
// local rect of the primitive. In the future, this will support
// loading segment rects, and other rect formats (glyphs).
PrimitiveGeometry geom = fetch_primitive_geometry(brush.prim_address);
// Write the (p0,p1) form of the primitive rect and the local position
// of this vertex. Specific brush shaders can use this information to
// interpolate texture coordinates etc.
vLocalRect = vec4(geom.local_rect.p0, geom.local_rect.p0 + geom.local_rect.size);
// Right now - pictures only support local positions. In the future, this
// will be expanded to support transform picture types (the common kind).
vec2 pos = pic_task.target_rect.p0 + aPosition.xy * pic_task.target_rect.size;
vLocalPos = aPosition.xy * pic_task.target_rect.size / uDevicePixelRatio;
// Run the specific brush VS code to write interpolators.
brush_vs(brush.prim_address, vLocalRect);
// Write the final position transformed by the orthographic device-pixel projection.
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
// Run the specific brush FS code to output the color.
vec4 color = brush_fs(vLocalPos, vLocalRect);
// TODO(gw): Handle pre-multiply common code here as required.
oFragColor = color;
}
#endif

Просмотреть файл

@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include shared,prim_shared,ellipse
flat varying float vClipMode;
flat varying vec4 vClipCenter_Radius_TL;
flat varying vec4 vClipCenter_Radius_TR;
flat varying vec4 vClipCenter_Radius_BR;
flat varying vec4 vClipCenter_Radius_BL;
#ifdef WR_VERTEX_SHADER
struct BrushPrimitive {
float clip_mode;
float radius;
};
BrushPrimitive fetch_brush_primitive(int address) {
vec4 data = fetch_from_resource_cache_1(address);
return BrushPrimitive(data.x, data.y);
}
void brush_vs(int prim_address, vec4 prim_rect) {
// Load the specific primitive.
BrushPrimitive prim = fetch_brush_primitive(prim_address + 2);
// Write clip parameters
vClipMode = prim.clip_mode;
vec2 r = vec2(prim.radius);
vClipCenter_Radius_TL = vec4(prim_rect.xy + vec2(r.x, r.y), r);
vClipCenter_Radius_TR = vec4(prim_rect.zy + vec2(-r.x, r.y), r);
vClipCenter_Radius_BR = vec4(prim_rect.zw + vec2(-r.x, -r.y), r);
vClipCenter_Radius_BL = vec4(prim_rect.xw + vec2(r.x, -r.y), r);
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 brush_fs(vec2 local_pos, vec4 local_rect) {
// TODO(gw): The mask code below is super-inefficient. Once we
// start using primitive segments in brush shaders, this can
// be made much faster.
float d = 0.0;
// Check if in valid clip region.
if (local_pos.x >= local_rect.x && local_pos.x < local_rect.z &&
local_pos.y >= local_rect.y && local_pos.y < local_rect.w) {
// Apply ellipse clip on each corner.
d = rounded_rect(local_pos,
vClipCenter_Radius_TL,
vClipCenter_Radius_TR,
vClipCenter_Radius_BR,
vClipCenter_Radius_BL);
}
return vec4(mix(d, 1.0 - d, vClipMode));
}
#endif
#include brush

Просмотреть файл

@ -31,7 +31,11 @@ void main(void) {
local_rect.xy + local_rect.zw,
aPosition.xy);
#if defined WR_FEATURE_COLOR
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0).xy);
#else
vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
#endif
vUv.z = src_task.data1.x;
vBlurRadius = 3 * int(task.data1.y);
vSigma = task.data1.y;
@ -58,6 +62,15 @@ void main(void) {
#endif
#ifdef WR_FRAGMENT_SHADER
#if defined WR_FEATURE_COLOR
#define SAMPLE_TYPE vec4
#define SAMPLE_TEXTURE(uv) texture(sCacheRGBA8, uv)
#else
#define SAMPLE_TYPE float
#define SAMPLE_TEXTURE(uv) texture(sCacheA8, uv).r
#endif
// TODO(gw): Write a fast path blur that handles smaller blur radii
// with a offset / weight uniform table and a constant
// loop iteration count!
@ -66,13 +79,13 @@ void main(void) {
// the number of texture fetches needed for a gaussian blur.
void main(void) {
vec4 original_color = texture(sCacheRGBA8, vUv);
SAMPLE_TYPE original_color = SAMPLE_TEXTURE(vUv);
// TODO(gw): The gauss function gets NaNs when blur radius
// is zero. In the future, detect this earlier
// and skip the blur passes completely.
if (vBlurRadius == 0) {
oFragColor = original_color;
oFragColor = vec4(original_color);
return;
}
@ -83,7 +96,7 @@ void main(void) {
gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y;
float gauss_coefficient_sum = 0.0;
vec4 avg_color = original_color * gauss_coefficient.x;
SAMPLE_TYPE avg_color = original_color * gauss_coefficient.x;
gauss_coefficient_sum += gauss_coefficient.x;
gauss_coefficient.xy *= gauss_coefficient.yz;
@ -91,15 +104,15 @@ void main(void) {
vec2 offset = vOffsetScale * float(i);
vec2 st0 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw);
avg_color += texture(sCacheRGBA8, vec3(st0, vUv.z)) * gauss_coefficient.x;
avg_color += SAMPLE_TEXTURE(vec3(st0, vUv.z)) * gauss_coefficient.x;
vec2 st1 = clamp(vUv.xy + offset, vUvRect.xy, vUvRect.zw);
avg_color += texture(sCacheRGBA8, vec3(st1, vUv.z)) * gauss_coefficient.x;
avg_color += SAMPLE_TEXTURE(vec3(st1, vUv.z)) * gauss_coefficient.x;
gauss_coefficient_sum += 2.0 * gauss_coefficient.x;
gauss_coefficient.xy *= gauss_coefficient.yz;
}
oFragColor = avg_color / gauss_coefficient_sum;
oFragColor = vec4(avg_color) / gauss_coefficient_sum;
}
#endif

Просмотреть файл

@ -1,188 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include shared,prim_shared
varying vec2 vPos;
flat varying vec2 vBorderRadii;
flat varying float vBlurRadius;
flat varying vec4 vBoxShadowRect;
flat varying float vInverted;
#ifdef WR_VERTEX_SHADER
in ivec2 aPrimAddress;
in int aTaskIndex;
void main(void) {
RenderTaskData task = fetch_render_task(aTaskIndex);
BoxShadow bs = fetch_boxshadow_direct(ivec2(aPrimAddress.x + VECS_PER_PRIM_HEADER, aPrimAddress.y));
vec2 p0 = task.data0.xy;
vec2 p1 = p0 + task.data0.zw;
vec2 pos = mix(p0, p1, aPosition.xy);
vBorderRadii = bs.border_radius_edge_size_blur_radius_inverted.xx;
vBlurRadius = bs.border_radius_edge_size_blur_radius_inverted.z;
vInverted = bs.border_radius_edge_size_blur_radius_inverted.w;
vBoxShadowRect = vec4(bs.bs_rect.xy, bs.bs_rect.xy + bs.bs_rect.zw);
// The fragment shader expects logical units, beginning at where the
// blur radius begins.
// The first path of the equation gets the virtual position in
// logical pixels within the patch rectangle (accounting for
// bilinear offset). Then we add the start position of the
// box shadow rect and subtract the blur radius to get the
// virtual coordinates that the FS expects.
vPos = (pos - 1.0 - p0) / uDevicePixelRatio + bs.bs_rect.xy - vec2(2.0 * vBlurRadius);
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
// See http://asciimath.org to render the equations here.
// The Gaussian function used for blurring:
//
// G_sigma(x) = 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2))
float gauss(float x, float sigma) {
float sigmaPow2 = sigma * sigma;
return 1.0 / sqrt(6.283185307179586 * sigmaPow2) * exp(-(x * x) / (2.0 * sigmaPow2));
}
// An approximation of the error function, which is related to the integral of the Gaussian
// function:
//
// "erf"(x) = 2/sqrt(pi) int_0^x e^(-t^2) dt
// ~~ 1 - 1 / (1 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4)^4
//
// where:
//
// a_1 = 0.278393, a_2 = 0.230389, a_3 = 0.000972, a_4 = 0.078108
//
// This approximation is accurate to `5 xx 10^-4`, more than accurate enough for our purposes.
//
// See: https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions
float erf(float x) {
bool negative = x < 0.0;
if (negative)
x = -x;
float x2 = x * x;
float x3 = x2 * x;
float x4 = x2 * x2;
float denom = 1.0 + 0.278393 * x + 0.230389 * x2 + 0.000972 * x3 + 0.078108 * x4;
float result = 1.0 - 1.0 / (denom * denom * denom * denom);
return negative ? -result : result;
}
// A useful helper for calculating integrals of the Gaussian function via the error function:
//
// "erf"_sigma(x) = 2 int 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) dx
// = "erf"(x/(sigma sqrt(2)))
float erfSigma(float x, float sigma) {
return erf(x / (sigma * 1.4142135623730951));
}
// Returns the blurred color value from the box itself (not counting any rounded corners). `p_0` is
// the vector distance to the top left corner of the box; `p_1` is the vector distance to its
// bottom right corner.
//
// "colorFromRect"_sigma(p_0, p_1)
// = int_{p_{0_y}}^{p_{1_y}} int_{p_{1_x}}^{p_{0_x}} G_sigma(y) G_sigma(x) dx dy
// = 1/4 ("erf"_sigma(p_{1_x}) - "erf"_sigma(p_{0_x}))
// ("erf"_sigma(p_{1_y}) - "erf"_sigma(p_{0_y}))
float colorFromRect(vec2 p0, vec2 p1, float sigma) {
return (erfSigma(p1.x, sigma) - erfSigma(p0.x, sigma)) *
(erfSigma(p1.y, sigma) - erfSigma(p0.y, sigma)) / 4.0;
}
// Returns the `x` coordinate on the ellipse with the given radii for the given `y` coordinate:
//
// "ellipsePoint"(y, y_0, a, b) = a sqrt(1 - ((y - y_0) / b)^2)
float ellipsePoint(float y, float y0, vec2 radii) {
float bStep = (y - y0) / radii.y;
return radii.x * sqrt(1.0 - bStep * bStep);
}
// A helper function to compute the value that needs to be subtracted to accommodate the border
// corners.
//
// "colorCutout"_sigma(x_{0_l}, x_{0_r}, y_0, y_{min}, y_{max}, a, b)
// = int_{y_{min}}^{y_{max}}
// int_{x_{0_r} + "ellipsePoint"(y, y_0, a, b)}^{x_{0_r} + a} G_sigma(y) G_sigma(x) dx
// + int_{x_{0_l} - a}^{x_{0_l} - "ellipsePoint"(y, y_0, a, b)} G_sigma(y) G_sigma(x)
// dx dy
// = int_{y_{min}}^{y_{max}} 1/2 G_sigma(y)
// ("erf"_sigma(x_{0_r} + a) - "erf"_sigma(x_{0_r} + "ellipsePoint"(y, y_0, a, b)) +
// "erf"_sigma(x_{0_l} - "ellipsePoint"(y, y_0, a, b)) - "erf"_sigma(x_{0_l} - a))
//
// with the outer integral evaluated numerically.
float colorCutoutGeneral(float x0l,
float x0r,
float y0,
float yMin,
float yMax,
vec2 radii,
float sigma) {
float sum = 0.0;
for (float y = yMin; y <= yMax; y += 1.0) {
float xEllipsePoint = ellipsePoint(y, y0, radii);
sum += gauss(y, sigma) *
(erfSigma(x0r + radii.x, sigma) - erfSigma(x0r + xEllipsePoint, sigma) +
erfSigma(x0l - xEllipsePoint, sigma) - erfSigma(x0l - radii.x, sigma));
}
return sum / 2.0;
}
// The value that needs to be subtracted to accommodate the top border corners.
float colorCutoutTop(float x0l, float x0r, float y0, vec2 radii, float sigma) {
return colorCutoutGeneral(x0l, x0r, y0, y0, y0 + radii.y, radii, sigma);
}
// The value that needs to be subtracted to accommodate the bottom border corners.
float colorCutoutBottom(float x0l, float x0r, float y0, vec2 radii, float sigma) {
return colorCutoutGeneral(x0l, x0r, y0, y0 - radii.y, y0, radii, sigma);
}
// The blurred color value for the point at `pos` with the top left corner of the box at
// `p_{0_"rect"}` and the bottom right corner of the box at `p_{1_"rect"}`.
float color(vec2 pos, vec2 p0Rect, vec2 p1Rect, vec2 radii, float sigma) {
// Compute the vector distances `p_0` and `p_1`.
vec2 p0 = p0Rect - pos, p1 = p1Rect - pos;
// Compute the basic color `"colorFromRect"_sigma(p_0, p_1)`. This is all we have to do if
// the box is unrounded.
float cRect = colorFromRect(p0, p1, sigma);
if (radii.x == 0.0 || radii.y == 0.0)
return cRect;
// Compute the inner corners of the box, taking border radii into account: `x_{0_l}`,
// `y_{0_t}`, `x_{0_r}`, and `y_{0_b}`.
float x0l = p0.x + radii.x;
float y0t = p1.y - radii.y;
float x0r = p1.x - radii.x;
float y0b = p0.y + radii.y;
// Compute the final color:
//
// "colorFromRect"_sigma(p_0, p_1) -
// ("colorCutoutTop"_sigma(x_{0_l}, x_{0_r}, y_{0_t}, a, b) +
// "colorCutoutBottom"_sigma(x_{0_l}, x_{0_r}, y_{0_b}, a, b))
float cCutoutTop = colorCutoutTop(x0l, x0r, y0t, radii, sigma);
float cCutoutBottom = colorCutoutBottom(x0l, x0r, y0b, radii, sigma);
return cRect - (cCutoutTop + cCutoutBottom);
}
void main(void) {
vec2 pos = vPos.xy;
vec2 p0Rect = vBoxShadowRect.xy, p1Rect = vBoxShadowRect.zw;
vec2 radii = vBorderRadii.xy;
float sigma = vBlurRadius / 2.0;
float value = color(pos, p0Rect, p1Rect, radii, sigma);
value = max(value, 0.0);
oFragColor = dither(vec4(vInverted == 1.0 ? 1.0 - value : value));
}
#endif

Просмотреть файл

@ -155,8 +155,7 @@ void main(void) {
clip_relative_pos);
// Get AA widths based on zoom / scale etc.
vec2 fw = fwidth(local_pos);
float afwidth = length(fw);
float aa_range = compute_aa_range(local_pos);
// SDF subtract edges for dash clip
float dash_distance = max(d0, -d1);
@ -167,8 +166,8 @@ void main(void) {
// Select between dot/dash clip based on mode.
float d = mix(dash_distance, dot_distance, vAlphaMask.x);
// Apply AA over half a device pixel for the clip.
d = 1.0 - smoothstep(0.0, 0.5 * afwidth, d);
// Apply AA.
d = distance_aa(aa_range, d);
// Completely mask out clip if zero'ing out the rect.
d = d * vAlphaMask.y;

Просмотреть файл

@ -81,76 +81,35 @@ void main(void) {
RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect);
vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy,
clip.top_left.outer_inner_radius.xy);
vec2 r_tl = clip.top_left.outer_inner_radius.xy;
vec2 r_tr = clip.top_right.outer_inner_radius.xy;
vec2 r_br = clip.bottom_right.outer_inner_radius.xy;
vec2 r_bl = clip.bottom_left.outer_inner_radius.xy;
vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x,
clip_rect.p0.y + clip.top_right.outer_inner_radius.y,
clip.top_right.outer_inner_radius.xy);
vClipCenter_Radius_TL = vec4(clip_rect.p0 + r_tl, r_tl);
vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy,
clip.bottom_right.outer_inner_radius.xy);
vClipCenter_Radius_TR = vec4(clip_rect.p1.x - r_tr.x,
clip_rect.p0.y + r_tr.y,
r_tr);
vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x,
clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y,
clip.bottom_left.outer_inner_radius.xy);
vClipCenter_Radius_BR = vec4(clip_rect.p1 - r_br, r_br);
vClipCenter_Radius_BL = vec4(clip_rect.p0.x + r_bl.x,
clip_rect.p1.y - r_bl.y,
r_bl);
}
#endif
#ifdef WR_FRAGMENT_SHADER
float clip_against_ellipse_if_needed(vec2 pos,
float current_distance,
vec4 ellipse_center_radius,
vec2 sign_modifier,
float afwidth) {
float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy,
ellipse_center_radius.zw);
return mix(current_distance,
ellipse_distance + afwidth,
all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy)));
}
float rounded_rect(vec2 pos) {
float current_distance = 0.0;
// Apply AA
float afwidth = 0.5 * length(fwidth(pos));
// Clip against each ellipse.
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_TL,
vec2(1.0),
afwidth);
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_TR,
vec2(-1.0, 1.0),
afwidth);
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_BR,
vec2(-1.0),
afwidth);
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_BL,
vec2(1.0, -1.0),
afwidth);
return smoothstep(0.0, afwidth, 1.0 - current_distance);
}
void main(void) {
float alpha = 1.f;
vec2 local_pos = init_transform_fs(vPos, alpha);
float clip_alpha = rounded_rect(local_pos);
float clip_alpha = rounded_rect(local_pos,
vClipCenter_Radius_TL,
vClipCenter_Radius_TR,
vClipCenter_Radius_BR,
vClipCenter_Radius_BL);
float combined_alpha = min(alpha, clip_alpha);

Просмотреть файл

@ -18,16 +18,16 @@ void main(void) {
int glyph_index = prim.user_data0;
int resource_address = prim.user_data1;
int text_shadow_address = prim.user_data2;
int picture_address = prim.user_data2;
// Fetch the parent text-shadow for this primitive. This allows the code
// Fetch the owning picture for this primitive. This allows the code
// below to normalize the glyph offsets relative to the original text
// shadow rect, which is the union of all elements that make up this
// text shadow. This allows the text shadow to be rendered at an
// arbitrary location in a render target (provided by the render
// task render_target_origin field).
PrimitiveGeometry shadow_geom = fetch_primitive_geometry(text_shadow_address);
TextShadow shadow = fetch_text_shadow(text_shadow_address + VECS_PER_PRIM_HEADER);
PrimitiveGeometry shadow_geom = fetch_primitive_geometry(picture_address);
Picture pic = fetch_picture(picture_address + VECS_PER_PRIM_HEADER);
Glyph glyph = fetch_glyph(prim.specific_prim_address,
glyph_index,
@ -41,7 +41,7 @@ void main(void) {
vec2 size = (res.uv_rect.zw - res.uv_rect.xy) * res.scale;
vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
vec2 origin = prim.task.render_target_origin +
uDevicePixelRatio * (local_pos + shadow.offset - shadow_geom.local_rect.p0);
uDevicePixelRatio * (local_pos + pic.offset - shadow_geom.local_rect.p0);
vec4 local_rect = vec4(origin, size);
vec2 texture_size = vec2(textureSize(sColor0, 0));
@ -53,7 +53,7 @@ void main(void) {
aPosition.xy);
vUv = vec3(mix(st0, st1, aPosition.xy), res.layer);
vColor = shadow.color;
vColor = pic.color;
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
}

Просмотреть файл

@ -66,4 +66,55 @@ float distance_to_ellipse(vec2 p, vec2 radii) {
}
}
float clip_against_ellipse_if_needed(
vec2 pos,
float current_distance,
vec4 ellipse_center_radius,
vec2 sign_modifier
) {
float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy,
ellipse_center_radius.zw);
return mix(current_distance,
ellipse_distance,
all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy)));
}
float rounded_rect(vec2 pos,
vec4 clip_center_radius_tl,
vec4 clip_center_radius_tr,
vec4 clip_center_radius_br,
vec4 clip_center_radius_bl) {
// Start with a negative value (means "inside") for all fragments that are not
// in a corner. If the fragment is in a corner, one of the clip_against_ellipse_if_needed
// calls below will update it.
float current_distance = -1.0;
// Clip against each ellipse.
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
clip_center_radius_tl,
vec2(1.0));
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
clip_center_radius_tr,
vec2(-1.0, 1.0));
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
clip_center_radius_br,
vec2(-1.0));
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
clip_center_radius_bl,
vec2(1.0, -1.0));
// Apply AA
// See comment in ps_border_corner about the choice of constants.
float aa_range = compute_aa_range(pos);
return distance_aa(aa_range, current_distance);
}
#endif

Просмотреть файл

@ -659,15 +659,15 @@ Rectangle fetch_rectangle(int address) {
return Rectangle(data);
}
struct TextShadow {
struct Picture {
vec4 color;
vec2 offset;
float blur_radius;
};
TextShadow fetch_text_shadow(int address) {
Picture fetch_picture(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return TextShadow(data[0], data[1].xy, data[1].z);
return Picture(data[0], data[1].xy, data[1].z);
}
struct TextRun {
@ -692,23 +692,6 @@ Image fetch_image(int address) {
return Image(data[0], data[1]);
}
struct BoxShadow {
vec4 src_rect;
vec4 bs_rect;
vec4 color;
vec4 border_radius_edge_size_blur_radius_inverted;
};
BoxShadow fetch_boxshadow(int address) {
vec4 data[4] = fetch_from_resource_cache_4(address);
return BoxShadow(data[0], data[1], data[2], data[3]);
}
BoxShadow fetch_boxshadow_direct(ivec2 address) {
vec4 data[4] = fetch_from_resource_cache_4_direct(address);
return BoxShadow(data[0], data[1], data[2], data[3]);
}
void write_clip(vec2 global_pos, ClipArea area) {
vec2 texture_size = vec2(textureSize(sSharedCacheA8, 0).xy);
vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
@ -719,6 +702,35 @@ void write_clip(vec2 global_pos, ClipArea area) {
#ifdef WR_FRAGMENT_SHADER
/// Find the appropriate half range to apply the AA smoothstep over.
/// This range represents a coefficient to go from one CSS pixel to half a device pixel.
float compute_aa_range(vec2 position) {
// The constant factor is chosen to compensate for the fact that length(fw) is equal
// to sqrt(2) times the device pixel ratio in the typical case. 0.5/sqrt(2) = 0.35355.
//
// This coefficient is chosen to ensure that any sample 0.5 pixels or more inside of
// the shape has no anti-aliasing applied to it (since pixels are sampled at their center,
// such a pixel (axis aligned) is fully inside the border). We need this so that antialiased
// curves properly connect with non-antialiased vertical or horizontal lines, among other things.
//
// Using larger aa steps is quite common when rendering shapes with distance fields.
// It gives a smoother (although blurrier look) by extending the range that is smoothed
// to produce the anti aliasing. In our case, however, extending the range inside of
// the shape causes noticeable artifacts at the junction between an antialiased corner
// and a straight edge.
// We may want to adjust this constant in specific scenarios (for example keep the principled
// value for straight edges where we want pixel-perfect equivalence with non antialiased lines
// when axis aligned, while selecting a larger and smoother aa range on curves).
return 0.35355 * length(fwidth(position));
}
/// Return the blending coefficient to for distance antialiasing.
///
/// 0.0 means inside the shape, 1.0 means outside.
float distance_aa(float aa_range, float signed_distance) {
return 1.0 - smoothstep(-aa_range, aa_range, signed_distance);
}
#ifdef WR_FEATURE_TRANSFORM
float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) {
vec2 d = max(p0 - pos, pos - p1);
@ -733,10 +745,10 @@ vec2 init_transform_fs(vec3 local_pos, out float fragment_alpha) {
float d = signed_distance_rect(pos, vLocalBounds.xy, vLocalBounds.zw);
// Find the appropriate distance to apply the AA smoothstep over.
float afwidth = 0.5 * length(fwidth(pos.xy));
float aa_range = compute_aa_range(pos.xy);
// Only apply AA to fragments outside the signed distance field.
fragment_alpha = 1.0 - smoothstep(0.0, afwidth, d);
fragment_alpha = distance_aa(aa_range, d);
return pos;
}

Просмотреть файл

@ -324,11 +324,7 @@ void main(void) {
alpha = min(alpha, do_clip());
// Find the appropriate distance to apply the AA smoothstep over.
// Using 0.7 instead of 0.5 for the step compensates for the fact that smoothstep
// is smooth at its endpoints and has a steeper maximum slope than a linear ramp.
vec2 fw = fwidth(local_pos);
float aa_step = 0.7 * length(fw);
float aa_range = compute_aa_range(local_pos);
float distance_for_color;
float color_mix_factor;
@ -349,29 +345,26 @@ void main(void) {
// To correct this exactly we would need to offset p by half a pixel in the
// direction of the center of the ellipse (a different offset for each corner).
// A half device pixel in css pixels (using the average of width and height in case
// there is any kind of transform applied).
float half_px = 0.25 * (fw.x + fw.y);
// Get signed distance from the inner/outer clips.
float d0 = distance_to_ellipse(p, vRadii0.xy) + half_px;
float d1 = distance_to_ellipse(p, vRadii0.zw) + half_px;
float d2 = distance_to_ellipse(p, vRadii1.xy) + half_px;
float d3 = distance_to_ellipse(p, vRadii1.zw) + half_px;
float d0 = distance_to_ellipse(p, vRadii0.xy);
float d1 = distance_to_ellipse(p, vRadii0.zw);
float d2 = distance_to_ellipse(p, vRadii1.xy);
float d3 = distance_to_ellipse(p, vRadii1.zw);
// SDF subtract main radii
float d_main = max(d0, aa_step - d1);
float d_main = max(d0, -d1);
// SDF subtract inner radii (double style borders)
float d_inner = max(d2 - aa_step, -d3);
float d_inner = max(d2, -d3);
// Select how to combine the SDF based on border style.
float d = mix(max(d_main, -d_inner), d_main, vSDFSelect);
// Only apply AA to fragments outside the signed distance field.
alpha = min(alpha, 1.0 - smoothstep(0.0, aa_step, d));
alpha = min(alpha, distance_aa(aa_range, d));
// Get the groove/ridge mix factor.
color_mix_factor = smoothstep(-aa_step, aa_step, -d2);
color_mix_factor = distance_aa(aa_range, d2);
} else {
// Handle the case where the fragment is outside the clip
// region in a corner. This occurs when border width is
@ -403,7 +396,7 @@ void main(void) {
// Select color based on side of line. Get distance from the
// reference line, and then apply AA along the edge.
float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos);
float m = smoothstep(-aa_step, aa_step, ld);
float m = distance_aa(aa_range, -ld);
vec4 color = mix(color0, color1, m);
oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);

Просмотреть файл

@ -253,8 +253,7 @@ void main(void) {
alpha = min(alpha, do_clip());
// Find the appropriate distance to apply the step over.
vec2 fw = fwidth(local_pos);
float afwidth = length(fw);
float aa_range = compute_aa_range(local_pos);
// Applies the math necessary to draw a style: double
// border. In the case of a solid border, the vertex
@ -291,9 +290,7 @@ void main(void) {
// Get the dot alpha
vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw;
float dot_distance = length(dot_relative_pos) - vClipParams.z;
float dot_alpha = 1.0 - smoothstep(-0.5 * afwidth,
0.5 * afwidth,
dot_distance);
float dot_alpha = distance_aa(aa_range, dot_distance);
// Select between dot/dash alpha based on clip mode.
alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect));

Просмотреть файл

@ -1,73 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include shared,prim_shared
flat varying vec4 vColor;
varying vec3 vUv;
flat varying vec2 vMirrorPoint;
flat varying vec4 vCacheUvRectCoords;
#ifdef WR_VERTEX_SHADER
#define BS_HEADER_VECS 4
RectWithSize fetch_instance_geometry(int address) {
vec4 data = fetch_from_resource_cache_1(address);
return RectWithSize(data.xy, data.zw);
}
void main(void) {
Primitive prim = load_primitive();
BoxShadow bs = fetch_boxshadow(prim.specific_prim_address);
RectWithSize segment_rect = fetch_instance_geometry(prim.specific_prim_address + BS_HEADER_VECS + prim.user_data0);
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect);
RenderTaskData child_task = fetch_render_task(prim.user_data1);
vUv.z = child_task.data1.x;
// Constant offsets to inset from bilinear filtering border.
vec2 patch_origin = child_task.data0.xy + vec2(1.0);
vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0);
vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio;
vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size;
vMirrorPoint = 0.5 * prim.local_rect.size / patch_size;
vec2 texture_size = vec2(textureSize(sSharedCacheA8, 0));
vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
vColor = bs.color;
write_clip(vi.screen_pos, prim.clip_area);
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
vec4 clip_scale = vec4(1.0, 1.0, 1.0, do_clip());
// Mirror and stretch the box shadow corner over the entire
// primitives.
vec2 uv = vMirrorPoint - abs(vUv.xy - vMirrorPoint);
// Ensure that we don't fetch texels outside the box
// shadow corner. This can happen, for example, when
// drawing the outer parts of an inset box shadow.
uv = clamp(uv, vec2(0.0), vec2(1.0));
// Map the unit UV to the actual UV rect in the cache.
uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv);
// Modulate the box shadow by the color.
float mask = texture(sSharedCacheA8, vec3(uv, vUv.z)).r;
oFragColor = clip_scale * dither(vColor * vec4(1.0, 1.0, 1.0, mask));
}
#endif

Просмотреть файл

@ -7,6 +7,10 @@
varying vec3 vUv;
flat varying vec4 vUvBounds;
#if defined WR_FEATURE_ALPHA
flat varying vec4 vColor;
#endif
#ifdef WR_VERTEX_SHADER
// Draw a cached primitive (e.g. a blurred text run) from the
// target cache to the framebuffer, applying tile clip boundaries.
@ -24,7 +28,14 @@ void main(void) {
RenderTaskData child_task = fetch_render_task(prim.user_data1);
vUv.z = child_task.data1.x;
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
#if defined WR_FEATURE_COLOR
vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
#else
Picture pic = fetch_picture(prim.specific_prim_address);
vec2 texture_size = vec2(textureSize(sColor1, 0).xy);
vColor = pic.color;
#endif
vec2 uv0 = child_task.data0.xy;
vec2 uv1 = (child_task.data0.xy + child_task.data0.zw);
@ -34,12 +45,32 @@ void main(void) {
uv1 / texture_size,
f);
vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy;
write_clip(vi.screen_pos, prim.clip_area);
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
oFragColor = texture(sColor0, vec3(uv, vUv.z));
#if defined WR_FEATURE_COLOR
vec4 color = texture(sColor0, vec3(uv, vUv.z));
#else
vec4 color = vColor * texture(sColor1, vec3(uv, vUv.z)).r;
#endif
// Un-premultiply the color from sampling the gradient.
if (color.a > 0.0) {
color.rgb /= color.a;
// Apply the clip mask
color.a = min(color.a, do_clip());
// Pre-multiply the result.
color.rgb *= color.a;
}
oFragColor = color;
}
#endif

Просмотреть файл

@ -98,19 +98,19 @@ void main(void) {
}
#ifdef WR_FEATURE_CACHE
int text_shadow_address = prim.user_data0;
PrimitiveGeometry shadow_geom = fetch_primitive_geometry(text_shadow_address);
TextShadow shadow = fetch_text_shadow(text_shadow_address + VECS_PER_PRIM_HEADER);
int picture_address = prim.user_data0;
PrimitiveGeometry picture_geom = fetch_primitive_geometry(picture_address);
Picture pic = fetch_picture(picture_address + VECS_PER_PRIM_HEADER);
vec2 device_origin = prim.task.render_target_origin +
uDevicePixelRatio * (prim.local_rect.p0 + shadow.offset - shadow_geom.local_rect.p0);
uDevicePixelRatio * (prim.local_rect.p0 + pic.offset - picture_geom.local_rect.p0);
vec2 device_size = uDevicePixelRatio * prim.local_rect.size;
vec2 device_pos = mix(device_origin,
device_origin + device_size,
aPosition.xy);
vColor = shadow.color;
vColor = pic.color;
vLocalPos = mix(prim.local_rect.p0,
prim.local_rect.p0 + prim.local_rect.size,
aPosition.xy);
@ -190,8 +190,7 @@ void main(void) {
#endif
// Find the appropriate distance to apply the step over.
vec2 fw = fwidth(local_pos);
float afwidth = length(fw);
float aa_range = compute_aa_range(local_pos);
// Select the x/y coord, depending on which axis this edge is.
vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
@ -215,9 +214,7 @@ void main(void) {
// Get the dot alpha
vec2 dot_relative_pos = vec2(x, pos.y) - vParams.yz;
float dot_distance = length(dot_relative_pos) - vParams.y;
alpha = min(alpha, 1.0 - smoothstep(-0.5 * afwidth,
0.5 * afwidth,
dot_distance));
alpha = min(alpha, distance_aa(aa_range, dot_distance));
break;
}
case LINE_STYLE_WAVY: {
@ -251,9 +248,7 @@ void main(void) {
float d = min(d1, d2);
// Apply AA based on the thickness of the wave.
alpha = 1.0 - smoothstep(vParams.x - 0.5 * afwidth,
vParams.x + 0.5 * afwidth,
d);
alpha = distance_aa(aa_range, d - vParams.x);
break;
}
}

Просмотреть файл

@ -89,10 +89,10 @@ void main(void) {
modulate_color = alpha * vColor;
break;
case MODE_SUBPX_PASS0:
modulate_color = vec4(alpha);
modulate_color = vec4(alpha) * vColor.a;
break;
case MODE_SUBPX_PASS1:
modulate_color = vColor;
modulate_color = alpha * vColor;
break;
}

Просмотреть файл

@ -2,16 +2,20 @@
* 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 api::{ClipId, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyFrameInfo};
use api::WorldPoint;
use clip::{ClipRegion, ClipSources, ClipSourcesHandle, ClipStore};
use clip_scroll_tree::TransformUpdateState;
use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
use geometry::ray_intersects_rect;
use spring::{Spring, DAMPING, STIFFNESS};
use tiling::PackedLayerIndex;
use util::MatrixHelpers;
use gpu_cache::GpuCache;
use render_task::{ClipChain, ClipChainNode, ClipWorkItem};
use resource_cache::ResourceCache;
use spring::{DAMPING, STIFFNESS, Spring};
use std::rc::Rc;
use tiling::{PackedLayer, PackedLayerIndex};
use util::{MatrixHelpers, MaxRect};
#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;
@ -27,6 +31,9 @@ pub struct ClipInfo {
/// The packed layer index for this node, which is used to render a clip mask
/// for it, if necessary.
pub packed_layer_index: PackedLayerIndex,
/// Whether or not this clip node automatically creates a mask.
pub is_masking: bool,
}
impl ClipInfo {
@ -35,9 +42,13 @@ impl ClipInfo {
packed_layer_index: PackedLayerIndex,
clip_store: &mut ClipStore,
) -> ClipInfo {
let clip_sources = ClipSources::from(clip_region);
let is_masking = clip_sources.is_masking();
ClipInfo {
clip_sources: clip_store.insert(ClipSources::from(clip_region)),
clip_sources: clip_store.insert(clip_sources),
packed_layer_index,
is_masking,
}
}
}
@ -102,9 +113,43 @@ pub struct ClipScrollNode {
/// Whether or not this node is a reference frame.
pub node_type: NodeType,
/// The node in the chain of clips that are necessary to clip display items
/// that have this ClipScrollNode as their clip parent. This will be used to
/// generate clip tasks.
pub clip_chain_node: ClipChain,
/// The intersected outer bounds of the clips for this node.
pub combined_clip_outer_bounds: DeviceIntRect,
/// The axis-aligned coordinate system id of this node.
pub coordinate_system_id: CoordinateSystemId,
}
impl ClipScrollNode {
fn new(
pipeline_id: PipelineId,
parent_id: Option<ClipId>,
rect: &LayerRect,
node_type: NodeType
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: *rect,
local_clip_rect: *rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: parent_id,
children: Vec::new(),
pipeline_id,
node_type: node_type,
clip_chain_node: None,
combined_clip_outer_bounds: DeviceIntRect::max_rect(),
coordinate_system_id: CoordinateSystemId(0),
}
}
pub fn new_scroll_frame(
pipeline_id: PipelineId,
parent_id: ClipId,
@ -112,24 +157,15 @@ impl ClipScrollNode {
content_size: &LayerSize,
scroll_sensitivity: ScrollSensitivity,
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: *frame_rect,
local_clip_rect: *frame_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id,
node_type: NodeType::ScrollFrame(ScrollingState::new(
scroll_sensitivity,
LayerSize::new(
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
)
)),
}
let node_type = NodeType::ScrollFrame(ScrollingState::new(
scroll_sensitivity,
LayerSize::new(
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
)
));
Self::new(pipeline_id, Some(parent_id), frame_rect, node_type)
}
pub fn new_clip_node(
@ -138,23 +174,12 @@ impl ClipScrollNode {
clip_info: ClipInfo,
clip_rect: LayerRect,
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: clip_rect,
local_clip_rect: clip_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id,
node_type: NodeType::Clip(clip_info),
}
Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(clip_info))
}
pub fn new_reference_frame(
parent_id: Option<ClipId>,
local_viewport_rect: &LayerRect,
frame_rect: &LayerRect,
transform: &LayerToScrollTransform,
origin_in_parent_reference_frame: LayerVector2D,
pipeline_id: PipelineId,
@ -163,19 +188,7 @@ impl ClipScrollNode {
transform: *transform,
origin_in_parent_reference_frame,
};
ClipScrollNode {
local_viewport_rect: *local_viewport_rect,
local_clip_rect: *local_viewport_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: parent_id,
children: Vec::new(),
pipeline_id,
node_type: NodeType::ReferenceFrame(info),
}
Self::new(pipeline_id, parent_id, frame_rect, NodeType::ReferenceFrame(info))
}
pub fn new_sticky_frame(
@ -184,18 +197,8 @@ impl ClipScrollNode {
sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId,
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: frame_rect,
local_clip_rect: frame_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id,
node_type: NodeType::StickyFrame(sticky_frame_info, LayerVector2D::zero()),
}
let node_type = NodeType::StickyFrame(sticky_frame_info, LayerVector2D::zero());
Self::new(pipeline_id, Some(parent_id), &frame_rect, node_type)
}
@ -255,7 +258,79 @@ impl ClipScrollNode {
true
}
pub fn update_transform(&mut self, state: &TransformUpdateState) {
pub fn update_clip_work_item(
&mut self,
state: &mut TransformUpdateState,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
packed_layers: &mut Vec<PackedLayer>,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
) {
self.coordinate_system_id = state.current_coordinate_system_id;
let current_clip_chain = state.parent_clip_chain.clone();
let clip_info = match self.node_type {
NodeType::Clip(ref mut info) if info.is_masking => info,
_ => {
self.clip_chain_node = current_clip_chain;
self.combined_clip_outer_bounds = state.combined_outer_clip_bounds;
return;
}
};
// The coordinates of the mask are relative to the origin of the node itself,
// so we need to account for that origin in the transformation we assign to
// the packed layer.
let transform = self.world_viewport_transform
.pre_translate(self.local_viewport_rect.origin.to_vector().to_3d());
let packed_layer = &mut packed_layers[clip_info.packed_layer_index.0];
if packed_layer.set_transform(transform) {
// Meanwhile, the combined viewport rect is relative to the reference frame, so
// we move it into the local coordinate system of the node.
let local_viewport_rect = self.combined_local_viewport_rect
.translate(&-self.local_viewport_rect.origin.to_vector());
packed_layer.set_rect(
&local_viewport_rect,
screen_rect,
device_pixel_ratio,
);
}
let clip_sources = clip_store.get_mut(&clip_info.clip_sources);
clip_sources.update(
&transform,
gpu_cache,
resource_cache,
device_pixel_ratio,
);
let outer_bounds = clip_sources.bounds.outer.as_ref().map_or_else(
DeviceIntRect::zero,
|rect| rect.device_rect
);
self.combined_clip_outer_bounds = outer_bounds.intersection(
&state.combined_outer_clip_bounds).unwrap_or_else(DeviceIntRect::zero);
// TODO: Combine rectangles in the same axis-aligned clip space here?
self.clip_chain_node = Some(Rc::new(ClipChainNode {
work_item: ClipWorkItem {
layer_index: clip_info.packed_layer_index,
clip_sources: clip_info.clip_sources.weak(),
coordinate_system_id: state.current_coordinate_system_id,
},
prev: current_clip_chain,
}));
state.combined_outer_clip_bounds = self.combined_clip_outer_bounds;
state.parent_clip_chain = self.clip_chain_node.clone();
}
pub fn update_transform(&mut self, state: &mut TransformUpdateState) {
// We calculate this here to avoid a double-borrow later.
let sticky_offset = self.calculate_sticky_offset(
&state.nearest_scrolling_ancestor_offset,
@ -315,6 +390,45 @@ impl ClipScrollNode {
let scroll_offset = self.scroll_offset();
self.world_content_transform = self.world_viewport_transform
.pre_translate(scroll_offset.to_3d());
// The transformation we are passing is the transformation of the parent
// reference frame and the offset is the accumulated offset of all the nodes
// between us and the parent reference frame. If we are a reference frame,
// we need to reset both these values.
match self.node_type {
NodeType::ReferenceFrame(ref info) => {
state.parent_reference_frame_transform = self.world_viewport_transform;
state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
state.parent_accumulated_scroll_offset = LayerVector2D::zero();
state.nearest_scrolling_ancestor_viewport =
state.nearest_scrolling_ancestor_viewport
.translate(&info.origin_in_parent_reference_frame);
if !info.transform.preserves_2d_axis_alignment() {
state.current_coordinate_system_id = state.next_coordinate_system_id;
state.next_coordinate_system_id = state.next_coordinate_system_id.next();
}
},
NodeType::Clip(..) => {
state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
},
NodeType::ScrollFrame(ref scrolling) => {
state.parent_combined_viewport_rect =
self.combined_local_viewport_rect.translate(&-scrolling.offset);
state.parent_accumulated_scroll_offset =
scrolling.offset + state.parent_accumulated_scroll_offset;
state.nearest_scrolling_ancestor_offset = scrolling.offset;
state.nearest_scrolling_ancestor_viewport = self.local_viewport_rect;
}
NodeType::StickyFrame(_, sticky_offset) => {
// We don't translate the combined rect by the sticky offset, because sticky
// offsets actually adjust the node position itself, whereas scroll offsets
// only apply to contents inside the node.
state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
state.parent_accumulated_scroll_offset =
sticky_offset + state.parent_accumulated_scroll_offset;
}
}
}
fn calculate_sticky_offset(

Просмотреть файл

@ -2,16 +2,35 @@
* 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 api::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform, LayerToWorldTransform};
use api::{LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
use api::{ScrollLocation, StickyFrameInfo, WorldPoint};
use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation, StickyFrameInfo};
use api::WorldPoint;
use clip::ClipStore;
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState};
use gpu_cache::GpuCache;
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use render_task::ClipChain;
use resource_cache::ResourceCache;
use tiling::PackedLayer;
pub type ScrollStates = FastHashMap<ClipId, ScrollingState>;
/// An id that identifies coordinate systems in the ClipScrollTree. Each
/// coordinate system has an id and those ids will be shared when the coordinates
/// system are the same or are in the same axis-aligned space. This allows
/// for optimizing mask generation.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct CoordinateSystemId(pub u32);
impl CoordinateSystemId {
pub fn next(&self) -> CoordinateSystemId {
let CoordinateSystemId(id) = *self;
CoordinateSystemId(id + 1)
}
}
pub struct ClipScrollTree {
pub nodes: FastHashMap<ClipId, ClipScrollNode>,
pub pending_scroll_offsets: FastHashMap<ClipId, (LayerPoint, ScrollClamping)>,
@ -38,12 +57,22 @@ pub struct ClipScrollTree {
pub pipelines_to_discard: FastHashSet<PipelineId>,
}
#[derive(Clone)]
pub struct TransformUpdateState {
pub parent_reference_frame_transform: LayerToWorldTransform,
pub parent_combined_viewport_rect: LayerRect,
pub parent_accumulated_scroll_offset: LayerVector2D,
pub nearest_scrolling_ancestor_offset: LayerVector2D,
pub nearest_scrolling_ancestor_viewport: LayerRect,
pub parent_clip_chain: ClipChain,
pub combined_outer_clip_bounds: DeviceIntRect,
/// An id for keeping track of the axis-aligned space of this node. This is used in
/// order to to track what kinds of clip optimizations can be done for a particular
/// display list item, since optimizations can usually only be done among
/// coordinate systems which are relatively axis aligned.
pub current_coordinate_system_id: CoordinateSystemId,
pub next_coordinate_system_id: CoordinateSystemId,
}
impl ClipScrollTree {
@ -297,14 +326,24 @@ impl ClipScrollTree {
.scroll(scroll_location, phase)
}
pub fn update_all_node_transforms(&mut self, pan: LayerPoint) {
pub fn update_all_node_transforms(
&mut self,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
packed_layers: &mut Vec<PackedLayer>,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
pan: LayerPoint,
) {
if self.nodes.is_empty() {
return;
}
let root_reference_frame_id = self.root_reference_frame_id();
let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
let state = TransformUpdateState {
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayerToWorldTransform::create_translation(
pan.x,
pan.y,
@ -314,63 +353,68 @@ impl ClipScrollTree {
parent_accumulated_scroll_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayerRect::zero(),
parent_clip_chain: None,
combined_outer_clip_bounds: *screen_rect,
current_coordinate_system_id: CoordinateSystemId(0),
next_coordinate_system_id: CoordinateSystemId(0).next(),
};
self.update_node_transform(root_reference_frame_id, &state);
self.update_node_transform(
root_reference_frame_id,
&mut state,
&screen_rect,
device_pixel_ratio,
packed_layers,
clip_store,
resource_cache,
gpu_cache,
);
}
fn update_node_transform(&mut self, layer_id: ClipId, state: &TransformUpdateState) {
fn update_node_transform(
&mut self,
layer_id: ClipId,
state: &mut TransformUpdateState,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
packed_layers: &mut Vec<PackedLayer>,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
) {
// TODO(gw): This is an ugly borrow check workaround to clone these.
// Restructure this to avoid the clones!
let (state, node_children) = {
let mut state = state.clone();
let node_children = {
let node = match self.nodes.get_mut(&layer_id) {
Some(node) => node,
None => return,
};
node.update_transform(&state);
// The transformation we are passing is the transformation of the parent
// reference frame and the offset is the accumulated offset of all the nodes
// between us and the parent reference frame. If we are a reference frame,
// we need to reset both these values.
let state = match node.node_type {
NodeType::ReferenceFrame(ref info) => TransformUpdateState {
parent_reference_frame_transform: node.world_viewport_transform,
parent_combined_viewport_rect: node.combined_local_viewport_rect,
parent_accumulated_scroll_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_viewport: state
.nearest_scrolling_ancestor_viewport
.translate(&info.origin_in_parent_reference_frame),
..*state
},
NodeType::Clip(..) => TransformUpdateState {
parent_combined_viewport_rect: node.combined_local_viewport_rect,
..*state
},
NodeType::ScrollFrame(ref scrolling) => TransformUpdateState {
parent_combined_viewport_rect:
node.combined_local_viewport_rect.translate(&-scrolling.offset),
parent_accumulated_scroll_offset: scrolling.offset +
state.parent_accumulated_scroll_offset,
nearest_scrolling_ancestor_offset: scrolling.offset,
nearest_scrolling_ancestor_viewport: node.local_viewport_rect,
..*state
},
NodeType::StickyFrame(_, sticky_offset) => TransformUpdateState {
// We don't translate the combined rect by the sticky offset, because sticky
// offsets actually adjust the node position itself, whereas scroll offsets
// only apply to contents inside the node.
parent_combined_viewport_rect: node.combined_local_viewport_rect,
parent_accumulated_scroll_offset:
sticky_offset + state.parent_accumulated_scroll_offset,
..*state
}
};
node.update_transform(&mut state);
node.update_clip_work_item(
&mut state,
screen_rect,
device_pixel_ratio,
packed_layers,
clip_store,
resource_cache,
gpu_cache,
);
(state, node.children.clone())
node.children.clone()
};
for child_layer_id in node_children {
self.update_node_transform(child_layer_id, &state);
self.update_node_transform(
child_layer_id,
&mut state,
screen_rect,
device_pixel_ratio,
packed_layers,
clip_store,
resource_cache,
gpu_cache,
);
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше