author | Iris Hsiao <ihsiao@mozilla.com> |
Mon, 08 May 2017 11:10:13 +0800 | |
changeset 356898 | 22aaf8bad4df8e5f8c976f1521c213cb37e2dff5 |
parent 356820 | 17d8a1e278a9c54a6fdda9d390abce4077e55b20 (current diff) |
parent 356897 | 74ff52c38236a889ac008c612e4754f5dc68602a (diff) |
child 356967 | c3e5497cff1c995821b1c9320fa71f1ef9a8c30e |
push id | 31775 |
push user | ihsiao@mozilla.com |
push date | Mon, 08 May 2017 03:10:38 +0000 |
treeherder | mozilla-central@22aaf8bad4df [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 55.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
parser/html/nsHtml5AtomList.h | file | annotate | diff | comparison | revisions | |
parser/html/nsHtml5Atoms.cpp | file | annotate | diff | comparison | revisions | |
parser/html/nsHtml5Atoms.h | file | annotate | diff | comparison | revisions | |
toolkit/themes/linux/mozapps/extensions/dictionaryGeneric-16.png | file | annotate | diff | comparison | revisions | |
toolkit/themes/linux/mozapps/extensions/themeGeneric-16.png | file | annotate | diff | comparison | revisions | |
toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.png | file | annotate | diff | comparison | revisions | |
toolkit/themes/osx/mozapps/extensions/search.png | file | annotate | diff | comparison | revisions | |
toolkit/themes/osx/mozapps/extensions/themeGeneric-16.png | file | annotate | diff | comparison | revisions | |
toolkit/themes/windows/mozapps/extensions/dictionaryGeneric-16.png | file | annotate | diff | comparison | revisions | |
toolkit/themes/windows/mozapps/extensions/themeGeneric-16.png | file | annotate | diff | comparison | revisions |
--- a/browser/base/content/browser-addons.js +++ b/browser/base/content/browser-addons.js @@ -495,75 +495,68 @@ const gExtensionsNotifications = { // uninit() can race ahead of init() in some cases, if that happens, // we have no handler to remove. if (!this.initialized) { return; } ExtensionsUI.off("change", this.boundUpdate); }, + _createAddonButton(text, icon, callback) { + let button = document.createElement("toolbarbutton"); + button.setAttribute("label", text); + const DEFAULT_EXTENSION_ICON = + "chrome://mozapps/skin/extensions/extensionGeneric.svg"; + button.setAttribute("image", icon || DEFAULT_EXTENSION_ICON); + button.className = "addon-banner-item"; + + button.addEventListener("click", callback); + PanelUI.addonNotificationContainer.appendChild(button); + }, + updateAlerts() { let sideloaded = ExtensionsUI.sideloaded; let updates = ExtensionsUI.updates; if (sideloaded.size + updates.size == 0) { PanelUI.removeNotification("addon-alert"); } else { PanelUI.showBadgeOnlyNotification("addon-alert"); } - let container = document.getElementById("PanelUI-footer-addons"); + let container = PanelUI.addonNotificationContainer; while (container.firstChild) { container.firstChild.remove(); } - const DEFAULT_EXTENSION_ICON = - "chrome://mozapps/skin/extensions/extensionGeneric.svg"; let items = 0; for (let update of updates) { if (++items > 4) { break; } - - let button = document.createElement("toolbarbutton"); let text = gNavigatorBundle.getFormattedString("webextPerms.updateMenuItem", [update.addon.name]); - button.setAttribute("label", text); - - let icon = update.addon.iconURL || DEFAULT_EXTENSION_ICON; - button.setAttribute("image", icon); - - button.addEventListener("click", evt => { + this._createAddonButton(text, update.addon.iconURL, evt => { ExtensionsUI.showUpdate(gBrowser, update); }); - - container.appendChild(button); } let appName; for (let addon of sideloaded) { if (++items > 4) { break; } if (!appName) { let brandBundle = document.getElementById("bundle_brand"); appName = brandBundle.getString("brandShortName"); } - let button = document.createElement("toolbarbutton"); let text = gNavigatorBundle.getFormattedString("webextPerms.sideloadMenuItem", [addon.name, appName]); - button.setAttribute("label", text); - - let icon = addon.iconURL || DEFAULT_EXTENSION_ICON; - button.setAttribute("image", icon); - - button.addEventListener("click", evt => { + this._createAddonButton(text, addon.iconURL, evt => { ExtensionsUI.showSideloaded(gBrowser, addon); }); - - container.appendChild(button); } }, }; var LightWeightThemeWebInstaller = { init() { let mm = window.messageManager; mm.addMessageListener("LightWeightThemeWebInstaller:Install", this);
--- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -1182,17 +1182,18 @@ toolbarpaletteitem[place="palette"][hidd #customization-palette .toolbarpaletteitem-box { -moz-box-pack: center; -moz-box-flex: 1; width: 10em; max-width: 10em; } -#main-window[customizing=true] .PanelUI-notification-menu-item { +#main-window[customizing=true] .addon-banner-item, +#main-window[customizing=true] .panel-banner-item { display: none; } /* UI Tour */ @keyframes uitour-wobble { from { transform: rotate(0deg) translateX(3px) rotate(0deg);
--- a/browser/base/content/test/appUpdate/head.js +++ b/browser/base/content/test/appUpdate/head.js @@ -203,17 +203,17 @@ function processStep({notificationId, bu is(shownNotification, notificationId, "The right notification showed up."); if (shownNotification != notificationId) { if (cleanup) { yield cleanup(); } return; } - let notification = document.getElementById(`PanelUI-${notificationId}-notification`); + let notification = document.getElementById(`appMenu-${notificationId}-notification`); is(notification.hidden, false, `${notificationId} notification is showing`); if (beforeClick) { yield Task.spawn(beforeClick); } let buttonEl = document.getAnonymousElementByAttribute(notification, "anonid", button); buttonEl.click();
--- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -209,20 +209,16 @@ var whitelist = new Set([ {file: "chrome://global/skin/tree/sort-dsc.png", platforms: ["linux"]}, // Bug 1344267 {file: "chrome://marionette/content/test_anonymous_content.xul"}, {file: "chrome://marionette/content/test_dialog.properties"}, {file: "chrome://marionette/content/test_dialog.xul"}, // Bug 1348533 {file: "chrome://mozapps/skin/downloads/buttons.png", platforms: ["macosx"]}, {file: "chrome://mozapps/skin/downloads/downloadButtons.png", platforms: ["linux", "win"]}, - // Bug 1348555 - {file: "chrome://mozapps/skin/extensions/dictionaryGeneric-16.png"}, - {file: "chrome://mozapps/skin/extensions/search.png", platforms: ["macosx"]}, - {file: "chrome://mozapps/skin/extensions/themeGeneric-16.png"}, // Bug 1348556 {file: "chrome://mozapps/skin/plugins/pluginBlocked.png"}, // Bug 1348558 {file: "chrome://mozapps/skin/update/downloadButtons.png", platforms: ["linux"]}, // Bug 1348559 {file: "chrome://pippki/content/resetpassword.xul"}, // Bug 1351078 @@ -230,18 +226,16 @@ var whitelist = new Set([ // Bug 1351070 {file: "resource://gre/modules/ContentPrefInstance.jsm"}, // Bug 1351079 {file: "resource://gre/modules/ISO8601DateUtils.jsm"}, // Bug 1337345 {file: "resource://gre/modules/Manifest.jsm"}, // Bug 1351089 {file: "resource://gre/modules/PresentationDeviceInfoManager.jsm"}, - // Bug 1351658 - {file: "resource://gre/modules/PropertyListUtils.jsm", platforms: ["linux", "win"]}, // Bug 1351097 {file: "resource://gre/modules/accessibility/AccessFu.jsm"}, // Bug 1351637 {file: "resource://gre/modules/sdk/bootstrap.js"}, ].filter(item => ("isFromDevTools" in item) == isDevtools && (!item.skipNightly || !AppConstants.NIGHTLY_BUILD) &&
--- a/browser/base/content/test/webextensions/browser_extension_sideloading.js +++ b/browser/base/content/test/webextensions/browser_extension_sideloading.js @@ -180,17 +180,17 @@ add_task(function* () { // Check for the addons badge on the hamburger menu let menuButton = document.getElementById("PanelUI-menu-button"); is(menuButton.getAttribute("badge-status"), "addon-alert", "Should have addon alert badge"); // Find the menu entries for sideloaded extensions yield PanelUI.show(); - let addons = document.getElementById("PanelUI-footer-addons"); + let addons = PanelUI.addonNotificationContainer; is(addons.children.length, 4, "Have 4 menu entries for sideloaded extensions"); // Click the first sideloaded extension let popupPromise = promisePopupNotificationShown("addon-webext-permissions"); addons.children[0].click(); // When we get the permissions prompt, we should be at the extensions // list in about:addons @@ -221,17 +221,17 @@ add_task(function* () { is(addon3.userDisabled, true, "Addon 3 should still be disabled"); is(addon4.userDisabled, true, "Addon 4 should still be disabled"); yield BrowserTestUtils.removeTab(gBrowser.selectedTab); // Should still have 3 entries in the hamburger menu yield PanelUI.show(); - addons = document.getElementById("PanelUI-footer-addons"); + addons = PanelUI.addonNotificationContainer; is(addons.children.length, 3, "Have 3 menu entries for sideloaded extensions"); // Click the second sideloaded extension and wait for the notification popupPromise = promisePopupNotificationShown("addon-webext-permissions"); addons.children[0].click(); panel = yield popupPromise; // Again we should be at the extentions list in about:addons @@ -255,17 +255,17 @@ add_task(function* () { is(addon1.userDisabled, true, "Addon 1 should still be disabled"); is(addon2.userDisabled, false, "Addon 2 should now be enabled"); is(addon3.userDisabled, true, "Addon 3 should still be disabled"); is(addon4.userDisabled, true, "Addon 4 should still be disabled"); // Should still have 2 entries in the hamburger menu yield PanelUI.show(); - addons = document.getElementById("PanelUI-footer-addons"); + addons = PanelUI.addonNotificationContainer; is(addons.children.length, 2, "Have 2 menu entries for sideloaded extensions"); // Close the hamburger menu and go directly to the addons manager yield PanelUI.hide(); win = yield BrowserOpenAddonsMgr(VIEW); let list = win.document.getElementById("addon-list"); @@ -294,17 +294,17 @@ add_task(function* () { is(value, false, "userDisabled should be set on addon 3"); addon3 = yield AddonManager.getAddonByID(ID3); is(addon3.userDisabled, false, "Addon 3 should be enabled"); // Should still have 1 entry in the hamburger menu yield PanelUI.show(); - addons = document.getElementById("PanelUI-footer-addons"); + addons = PanelUI.addonNotificationContainer; is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions"); // Close the hamburger menu and go to the detail page for this addon yield PanelUI.hide(); win = yield BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(ID4)}`); let button = win.document.getElementById("detail-enable-btn");
--- a/browser/base/content/test/webextensions/browser_extension_update_background.js +++ b/browser/base/content/test/webextensions/browser_extension_update_background.js @@ -76,17 +76,17 @@ function* backgroundUpdateTest(url, id, AddonManagerPrivate.backgroundUpdateCheck(); yield updatePromise; is(getBadgeStatus(), "addon-alert", "Should have addon alert badge"); // Find the menu entry for the update yield PanelUI.show(); - let addons = document.getElementById("PanelUI-footer-addons"); + let addons = PanelUI.addonNotificationContainer; is(addons.children.length, 1, "Have a menu entry for the update"); // Click the menu item let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons"); let popupPromise = promisePopupNotificationShown("addon-webext-permissions"); addons.children[0].click(); // about:addons should load and go to the list of extensions @@ -116,31 +116,31 @@ function* backgroundUpdateTest(url, id, is(addon.version, "1.0", "Should still be running the old version"); yield BrowserTestUtils.removeTab(tab); // Alert badge and hamburger menu items should be gone is(getBadgeStatus(), "", "Addon alert badge should be gone"); yield PanelUI.show(); - addons = document.getElementById("PanelUI-footer-addons"); + addons = PanelUI.addonNotificationContainer; is(addons.children.length, 0, "Update menu entries should be gone"); yield PanelUI.hide(); // Re-check for an update updatePromise = promiseInstallEvent(addon, "onDownloadEnded"); yield AddonManagerPrivate.backgroundUpdateCheck(); yield updatePromise; is(getBadgeStatus(), "addon-alert", "Should have addon alert badge"); // Find the menu entry for the update yield PanelUI.show(); - addons = document.getElementById("PanelUI-footer-addons"); + addons = PanelUI.addonNotificationContainer; is(addons.children.length, 1, "Have a menu entry for the update"); // Click the menu item tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons"); popupPromise = promisePopupNotificationShown("addon-webext-permissions"); addons.children[0].click(); // Wait for about:addons to load
--- a/browser/base/content/test/webextensions/browser_extension_update_background_noprompt.js +++ b/browser/base/content/test/webextensions/browser_extension_update_background_noprompt.js @@ -54,17 +54,17 @@ async function testNoPrompt(origUrl, id) let updatePromise = waitForUpdate(addon); AddonManagerPrivate.backgroundUpdateCheck(); await updatePromise; // There should be no notifications about the update is(getBadgeStatus(), "", "Should not have addon alert badge"); await PanelUI.show(); - let addons = document.getElementById("PanelUI-footer-addons"); + let addons = PanelUI.addonNotificationContainer; is(addons.children.length, 0, "Have 0 updates in the PanelUI menu"); await PanelUI.hide(); ok(!sawPopup, "Should not have seen permissions notification"); addon = await AddonManager.getAddonByID(id); is(addon.version, "2.0", "Update should have applied");
--- a/browser/base/content/test/webrtc/browser.ini +++ b/browser/base/content/test/webrtc/browser.ini @@ -7,15 +7,14 @@ support-files = [browser_devices_get_user_media.js] skip-if = (os == "linux" && debug) # linux: bug 976544 [browser_devices_get_user_media_anim.js] [browser_devices_get_user_media_in_frame.js] [browser_devices_get_user_media_multi_process.js] skip-if = e10s && (asan || debug) # bug 1347625 [browser_devices_get_user_media_screen.js] -skip-if = (os == "linux") || (os == "win" && !debug) # bug 1320994 for linux opt, bug 1338038 for windows and linux debug [browser_devices_get_user_media_tear_off_tab.js] [browser_devices_get_user_media_unprompted_access.js] [browser_devices_get_user_media_unprompted_access_in_frame.js] [browser_devices_get_user_media_unprompted_access_tear_off_tab.js] -skip-if = (os == "linux") || (os == "win" && bits == 64) # linux: bug 1331616, win8: bug 1334752 +skip-if = (os == "win" && bits == 64) # win8: bug 1334752 [browser_webrtc_hooks.js]
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js +++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js @@ -50,17 +50,17 @@ var gTests = [ is(item.getAttribute("devicetype"), "Screen", "the devicetype attribute is set correctly"); ok(item.scary, "the screen item is marked as scary"); } // Select a screen, a preview with a scary warning should appear. menulist.getItemAtIndex(2).doCommand(); ok(!document.getElementById("webRTC-all-windows-shared").hidden, "the 'all windows will be shared' warning should now be visible"); - yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden); + yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden, 100); ok(!document.getElementById("webRTC-preview").hidden, "the preview area is visible"); ok(!document.getElementById("webRTC-previewWarning").hidden, "the scary warning is visible"); // Select the 'No Screen' item again, the preview should be hidden. menulist.getItemAtIndex(0).doCommand(); ok(document.getElementById("webRTC-all-windows-shared").hidden, @@ -405,30 +405,30 @@ var gTests = [ yield share(false, true); yield check({video: true, audio: true, screen: "Screen"}); info("Stop the screen share, mic+cam should continue"); yield stopSharing("screen", true); yield check({video: true, audio: true}); info("Stop the camera, everything should stop."); - yield stopSharing("camera", false, true); + yield stopSharing("camera"); info("Now, share only the screen..."); indicator = promiseIndicatorWindow(); yield share(false, false, true); yield indicator; yield check({screen: "Screen"}); info("... and add camera and microphone in a second request."); yield share(true, true); yield check({video: true, audio: true, screen: "Screen"}); info("Stop the camera, this should stop everything."); - yield stopSharing("camera", false, true); + yield stopSharing("camera"); } }, { desc: "getUserMedia screen: reloading the page removes all gUM UI", run: function* checkReloading() { let promise = promisePopupNotificationShown("webRTC-shareDevices"); yield promiseRequestDevice(false, true, null, "screen");
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_unprompted_access.js +++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_unprompted_access.js @@ -57,17 +57,17 @@ var gTests = [ }); yield expectObserverCalled("getUserMedia:response:deny"); SitePermissions.remove(null, "screen", gBrowser.selectedBrowser); SitePermissions.remove(null, "camera", gBrowser.selectedBrowser); SitePermissions.remove(null, "microphone", gBrowser.selectedBrowser); // After closing all streams, gUM(audio+camera) causes a prompt. - yield closeStream(false, 0, 2); + yield closeStream(); promise = promisePopupNotificationShown("webRTC-shareDevices"); yield promiseRequestDevice(true, true); yield promise; yield expectObserverCalled("getUserMedia:request"); checkDeviceSelectors(true, true); yield promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); @@ -164,17 +164,17 @@ var gTests = [ yield expectObserverCalled("recording-device-events"); Assert.deepEqual((yield getMediaCaptureState()), {video: true}, "expected camera to be shared"); yield checkSharingUI({audio: false, video: true}); // close all streams - yield closeStream(false, 0, 2); + yield closeStream(); } }, { desc: "getUserMedia audio", run: function* checkAudioVideoWhileLiveTracksExist_audio() { let promise = promisePopupNotificationShown("webRTC-shareDevices"); yield promiseRequestDevice(true, false); @@ -236,17 +236,17 @@ var gTests = [ yield expectObserverCalled("recording-device-events"); Assert.deepEqual((yield getMediaCaptureState()), {audio: true}, "expected microphone to be shared"); yield checkSharingUI({audio: true, video: false}); // close all streams - yield closeStream(false, 0, 2); + yield closeStream(); } } ]; add_task(async function test() { await runTests(gTests); });
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_unprompted_access_in_frame.js +++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_unprompted_access_in_frame.js @@ -52,17 +52,17 @@ var gTests = [ yield promiseRequestDevice(true, true, "frame1"); yield promise; yield expectObserverCalled("getUserMedia:request"); yield promiseNoPopupNotification("webRTC-shareDevices"); yield expectObserverCalled("getUserMedia:response:allow"); yield expectObserverCalled("recording-device-events"); // close the stream - yield closeStream(false, "frame1", 2); + yield closeStream(false, "frame1"); } }, { desc: "getUserMedia audio+camera in frame 1 - part II", run: function* checkAudioVideoWhileLiveTracksExist_frame_partII() { let promise = promisePopupNotificationShown("webRTC-shareDevices"); yield promiseRequestDevice(true, true, "frame1"); @@ -192,17 +192,17 @@ var gTests = [ yield promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); yield expectObserverCalled("getUserMedia:response:deny"); yield expectObserverCalled("recording-window-ended"); // close the stream - yield closeStream(false); + yield closeStream(); SitePermissions.remove(null, "screen", gBrowser.selectedBrowser); SitePermissions.remove(null, "camera", gBrowser.selectedBrowser); SitePermissions.remove(null, "microphone", gBrowser.selectedBrowser); } } ];
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_unprompted_access_tear_off_tab.js +++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_unprompted_access_tear_off_tab.js @@ -36,17 +36,16 @@ var gTests = [ yield promiseRequestDevice(true, true, null, null, win.gBrowser.selectedBrowser); yield promiseObserverCalled("getUserMedia:request"); let promises = [promiseNoPopupNotification("webRTC-shareDevices"), promiseObserverCalled("getUserMedia:response:allow"), promiseObserverCalled("recording-device-events")]; yield Promise.all(promises); promises = [promiseObserverCalled("recording-device-events"), - promiseObserverCalled("recording-device-events"), promiseObserverCalled("recording-window-ended")]; yield BrowserTestUtils.closeWindow(win); yield Promise.all(promises); yield checkNotSharing(); } }
--- a/browser/base/content/test/webrtc/head.js +++ b/browser/base/content/test/webrtc/head.js @@ -25,19 +25,19 @@ function waitForCondition(condition, nex if (conditionPassed) { moveOn(); } tries++; }, 100); var moveOn = function() { clearInterval(interval); nextTest(); }; } -function promiseWaitForCondition(aConditionFn) { +function promiseWaitForCondition(aConditionFn, retryTimes) { let deferred = Promise.defer(); - waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass."); + waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass.", retryTimes); return deferred.promise; } /** * Waits for a window with the given URL to exist. * * @param url * The url of the window. @@ -210,35 +210,27 @@ function expectObserverCalled(aTopic) { is(data.count, 1, "expected notification " + aTopic); mm.removeMessageListener("Test:ExpectObserverCalled:Reply", listener); resolve(); }); mm.sendAsyncMessage("Test:ExpectObserverCalled", aTopic); }); } -function expectNoObserverCalled(aIgnoreDeviceEvents = false) { +function expectNoObserverCalled() { return new Promise(resolve => { let mm = _mm(); mm.addMessageListener("Test:ExpectNoObserverCalled:Reply", function listener({data}) { mm.removeMessageListener("Test:ExpectNoObserverCalled:Reply", listener); for (let topic in data) { if (!data[topic]) continue; - // If we are stopping tracks that were created from 2 different - // getUserMedia calls, the "recording-device-events" notification is - // fired twice on Windows and Mac, and intermittently twice on Linux. - if (topic == "recording-device-events" && aIgnoreDeviceEvents) { - todo(false, "Got " + data[topic] + " unexpected " + topic + - " notifications, see bug 1320994"); - } else { - is(data[topic], 0, topic + " notification unexpected"); - } + is(data[topic], 0, topic + " notification unexpected"); } resolve(); }); mm.sendAsyncMessage("Test:ExpectNoObserverCalled"); }); } function ignoreObserversCalled() { @@ -349,35 +341,34 @@ function getMediaCaptureState() { let mm = _mm(); mm.addMessageListener("Test:MediaCaptureState", ({data}) => { resolve(data); }); mm.sendAsyncMessage("Test:GetMediaCaptureState"); }); } -function* stopSharing(aType = "camera", aShouldKeepSharing = false, - aExpectDoubleRecordingEvent = false) { +function* stopSharing(aType = "camera", aShouldKeepSharing = false) { let promiseRecordingEvent = promiseObserverCalled("recording-device-events"); gIdentityHandler._identityBox.click(); let permissions = document.getElementById("identity-popup-permission-list"); let cancelButton = permissions.querySelector(".identity-popup-permission-icon." + aType + "-icon ~ " + ".identity-popup-permission-remove-button"); cancelButton.click(); gIdentityHandler._identityPopup.hidden = true; yield promiseRecordingEvent; yield expectObserverCalled("getUserMedia:revoke"); // If we are stopping screen sharing and expect to still have another stream, // "recording-window-ended" won't be fired. if (!aShouldKeepSharing) yield expectObserverCalled("recording-window-ended"); - yield expectNoObserverCalled(aExpectDoubleRecordingEvent); + yield expectNoObserverCalled(); if (!aShouldKeepSharing) yield* checkNotSharing(); } function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId, aType, aBrowser = gBrowser.selectedBrowser) { info("requesting devices"); @@ -386,26 +377,23 @@ function promiseRequestDevice(aRequestAu function*(args) { let global = content.wrappedJSObject; if (args.aFrameId) global = global.document.getElementById(args.aFrameId).contentWindow; global.requestDevice(args.aRequestAudio, args.aRequestVideo, args.aType); }); } -function* closeStream(aAlreadyClosed, aFrameId, aStreamCount = 1) { +function* closeStream(aAlreadyClosed, aFrameId) { yield expectNoObserverCalled(); let promises; if (!aAlreadyClosed) { - promises = []; - for (let i = 0; i < aStreamCount; i++) { - promises.push(promiseObserverCalled("recording-device-events")); - } - promises.push(promiseObserverCalled("recording-window-ended")); + promises = [promiseObserverCalled("recording-device-events"), + promiseObserverCalled("recording-window-ended")]; } info("closing the stream"); yield ContentTask.spawn(gBrowser.selectedBrowser, aFrameId, function*(contentFrameId) { let global = content.wrappedJSObject; if (contentFrameId) global = global.document.getElementById(contentFrameId).contentWindow; global.closeStream();
--- a/browser/components/customizableui/content/panelUI.inc.xul +++ b/browser/components/customizableui/content/panelUI.inc.xul @@ -12,27 +12,22 @@ <panelmultiview id="PanelUI-multiView" mainViewId="PanelUI-mainView"> <panelview id="PanelUI-mainView" context="customizationPanelContextMenu"> <vbox id="PanelUI-contents-scroller"> <vbox id="PanelUI-contents" class="panelUI-grid"/> </vbox> <footer id="PanelUI-footer"> <vbox id="PanelUI-footer-addons"></vbox> - <toolbarbutton id="PanelUI-update-available-menu-item" - wrap="true" - label="&updateAvailable.panelUI.label;" - hidden="true"/> - <toolbarbutton id="PanelUI-update-manual-menu-item" + <toolbarbutton class="panel-banner-item" + label-update-available="&updateAvailable.panelUI.label;" + label-update-manual="&updateManual.panelUI.label;" + label-update-restart="&updateRestart.panelUI.label;" + oncommand="PanelUI._onBannerItemSelected(event)" wrap="true" - label="&updateManual.panelUI.label;" - hidden="true"/> - <toolbarbutton id="PanelUI-update-restart-menu-item" - wrap="true" - label="&updateRestart.panelUI.label;" hidden="true"/> <hbox id="PanelUI-footer-fxa"> <hbox id="PanelUI-fxa-status" label="&fxaSignedIn.tooltip;" defaultlabel="&fxaSignIn.label;" signedinTooltiptext="&fxaSignedIn.tooltip;" tooltiptext="&fxaSignedIn.tooltip;" errorlabel="&fxaSignInError.label;" @@ -426,27 +421,27 @@ <description>&panicButton.thankyou.msg2;</description> </vbox> </hbox> <button label="&panicButton.thankyou.buttonlabel;" id="panic-button-success-closebutton" oncommand="PanicButtonNotifier.close()"/> </panel> -<panel id="PanelUI-notification-popup" +<panel id="appMenu-notification-popup" class="popup-notification-panel" type="arrow" position="after_start" hidden="true" orient="vertical" noautofocus="true" noautohide="true" nopreventnavboxhide="true" role="alert"> - <popupnotification id="PanelUI-update-available-notification" + <popupnotification id="appMenu-update-available-notification" popupid="update-available" label="&updateAvailable.header.message;" buttonlabel="&updateAvailable.acceptButton.label;" buttonaccesskey="&updateAvailable.acceptButton.accesskey;" closebuttonhidden="true" secondarybuttonlabel="&updateAvailable.cancelButton.label;" secondarybuttonaccesskey="&updateAvailable.cancelButton.accesskey;" dropmarkerhidden="true" @@ -454,17 +449,17 @@ hidden="true"> <popupnotificationcontent id="update-available-notification-content" orient="vertical"> <description id="update-available-description">&updateAvailable.message; <label id="update-available-whats-new" class="text-link" value="&updateAvailable.whatsnew.label;" /> </description> </popupnotificationcontent> </popupnotification> - <popupnotification id="PanelUI-update-manual-notification" + <popupnotification id="appMenu-update-manual-notification" popupid="update-manual" label="&updateManual.header.message;" buttonlabel="&updateManual.acceptButton.label;" buttonaccesskey="&updateManual.acceptButton.accesskey;" closebuttonhidden="true" secondarybuttonlabel="&updateManual.cancelButton.label;" secondarybuttonaccesskey="&updateManual.cancelButton.accesskey;" dropmarkerhidden="true" @@ -472,17 +467,17 @@ hidden="true"> <popupnotificationcontent id="update-manual-notification-content" orient="vertical"> <description id="update-manual-description">&updateManual.message; <label id="update-manual-whats-new" class="text-link" value="&updateManual.whatsnew.label;" /> </description> </popupnotificationcontent> </popupnotification> - <popupnotification id="PanelUI-update-restart-notification" + <popupnotification id="appMenu-update-restart-notification" popupid="update-restart" label="&updateRestart.header.message2;" buttonlabel="&updateRestart.acceptButton.label;" buttonaccesskey="&updateRestart.acceptButton.accesskey;" closebuttonhidden="true" secondarybuttonlabel="&updateRestart.cancelButton.label;" secondarybuttonaccesskey="&updateRestart.cancelButton.accesskey;" dropmarkerhidden="true" @@ -500,16 +495,24 @@ type="arrow" hidden="true" flip="slide" position="bottomcenter topright" noautofocus="true"> <panelmultiview id="appMenu-multiView" mainViewId="appMenu-mainView"> <panelview id="appMenu-mainView" class="cui-widget-panelview PanelUI-subView"> <vbox class="panel-subview-body"> + <vbox id="appMenu-addon-banners"/> + <toolbarbutton class="panel-banner-item" + label-update-available="&updateAvailable.panelUI.label;" + label-update-manual="&updateManual.panelUI.label;" + label-update-restart="&updateRestart.panelUI.label;" + oncommand="PanelUI._onBannerItemSelected(event)" + wrap="true" + hidden="true"/> <toolbarbutton id="appMenu-new-window-button" class="subviewbutton subviewbutton-iconic" label="&newNavigatorCmd.label;" key="key_newNavigator" command="cmd_newNavigator"/> <toolbarbutton id="appMenu-private-window-button" class="subviewbutton subviewbutton-iconic" label="&newPrivateWindow.label;"
--- a/browser/components/customizableui/content/panelUI.js +++ b/browser/components/customizableui/content/panelUI.js @@ -30,19 +30,19 @@ const PanelUI = { get kElements() { return { contents: "PanelUI-contents", mainView: gPhotonStructure ? "appMenu-mainView" : "PanelUI-mainView", multiView: gPhotonStructure ? "appMenu-multiView" : "PanelUI-multiView", helpView: "PanelUI-helpView", menuButton: "PanelUI-menu-button", panel: gPhotonStructure ? "appMenu-popup" : "PanelUI-popup", - notificationPanel: "PanelUI-notification-popup", + notificationPanel: "appMenu-notification-popup", scroller: "PanelUI-contents-scroller", - footer: "PanelUI-footer", + addonNotificationContainer: gPhotonStructure ? "appMenu-addon-banners" : "PanelUI-footer-addons", overflowFixedList: gPhotonStructure ? "widget-overflow-fixed-list" : "", }; }, _initialized: false, init() { for (let [k, v] of Object.entries(this.kElements)) { @@ -698,31 +698,31 @@ const PanelUI = { if (this.panel.state == "showing" || this.panel.state == "open") { // If the menu is already showing, then we need to dismiss all notifications // since we don't want their doorhangers competing for attention doorhangers.forEach(n => { n.dismissed = true; }) this._hidePopup(); this._clearBadge(); if (!this.notifications[0].options.badgeOnly) { - this._showMenuItem(this.notifications[0]); + this._showBannerItem(this.notifications[0]); } } else if (doorhangers.length > 0) { if (window.fullScreen) { this._hidePopup(); this._showBadge(doorhangers[0]); - this._showMenuItem(doorhangers[0]); + this._showBannerItem(doorhangers[0]); } else { this._clearBadge(); this._showNotificationPanel(doorhangers[0]); } } else { this._hidePopup(); this._showBadge(this.notifications[0]); - this._showMenuItem(this.notifications[0]); + this._showBannerItem(this.notifications[0]); } }, _showNotificationPanel(notification) { this._refreshNotificationPanel(notification); if (this.isNotificationPanelOpen) { return; @@ -739,17 +739,17 @@ const PanelUI = { popupnotification.hidden = true; popupnotification.notification = null; } }, _clearAllNotifications() { this._clearNotificationPanel(); this._clearBadge(); - this._clearMenuItems(); + this._clearBannerItem(); }, _refreshNotificationPanel(notification) { this._clearNotificationPanel(); let popupnotificationID = this._getPopupId(notification); let popupnotification = document.getElementById(popupnotificationID); @@ -761,42 +761,41 @@ const PanelUI = { popupnotification.hidden = false; }, _showBadge(notification) { let badgeStatus = this._getBadgeStatus(notification); this.menuButton.setAttribute("badge-status", badgeStatus); }, - // "Menu item" here refers to an item in the hamburger panel menu. They will - // typically show up as a colored row near the bottom of the panel. - _showMenuItem(notification) { - this._clearMenuItems(); - - let menuItemId = this._getMenuItemId(notification); - let menuItem = document.getElementById(menuItemId); - if (menuItem) { - menuItem.notification = notification; - menuItem.setAttribute("oncommand", "PanelUI._onNotificationMenuItemSelected(event)"); - menuItem.classList.add("PanelUI-notification-menu-item"); - menuItem.hidden = false; - menuItem.fromPanelUINotifications = true; + // "Banner item" here refers to an item in the hamburger panel menu. They will + // typically show up as a colored row in the panel. + _showBannerItem(notification) { + if (!this._panelBannerItem) { + this._panelBannerItem = this.mainView.querySelector(".panel-banner-item"); } + let label = this._panelBannerItem.getAttribute("label-" + notification.id); + // Ignore items we don't know about. + if (!label) { + return; + } + this._panelBannerItem.setAttribute("notificationid", notification.id); + this._panelBannerItem.setAttribute("label", label); + this._panelBannerItem.hidden = false; + this._panelBannerItem.notification = notification; }, _clearBadge() { this.menuButton.removeAttribute("badge-status"); }, - _clearMenuItems() { - for (let child of this.footer.children) { - if (child.fromPanelUINotifications) { - child.notification = null; - child.hidden = true; - } + _clearBannerItem() { + if (this._panelBannerItem) { + this._panelBannerItem.notification = null; + this._panelBannerItem.hidden = true; } }, _removeNotification(notification) { // This notification may already be removed, in which case let's just fail // silently. let notifications = this.notifications; if (!notifications) @@ -847,17 +846,17 @@ const PanelUI = { notification.dismissed = true; this._notify(notification.id, "dismissed"); } else { this._removeNotification(notification); } this._updateNotifications(); }, - _onNotificationMenuItemSelected(event) { + _onBannerItemSelected(event) { let target = event.originalTarget; if (!target.notification) throw "menucommand target has no associated action/notification"; event.stopPropagation(); try { target.notification.mainAction.callback(false); @@ -865,22 +864,20 @@ const PanelUI = { } catch (error) { Cu.reportError(error); } this._removeNotification(target.notification); this._updateNotifications(); }, - _getPopupId(notification) { return "PanelUI-" + notification.id + "-notification"; }, + _getPopupId(notification) { return "appMenu-" + notification.id + "-notification"; }, _getBadgeStatus(notification) { return notification.id; }, - _getMenuItemId(notification) { return "PanelUI-" + notification.id + "-menu-item"; }, - _getPanelAnchor(candidate) { let iconAnchor = document.getAnonymousElementByAttribute(candidate, "class", "toolbarbutton-icon"); return iconAnchor || candidate; }, _addedShortcuts: false,
--- a/browser/components/customizableui/test/browser_panelUINotifications.js +++ b/browser/components/customizableui/test/browser_panelUINotifications.js @@ -27,17 +27,17 @@ add_task(function* testMainActionCalled( callback: () => { extraMainActionCalled = true; } }; extraWindow.PanelUI.showNotification("update-manual", extraMainAction) isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing."); let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden); is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification."); let doorhanger = notifications[0]; - is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification."); + is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification."); let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button"); mainActionButton.click(); ok(mainActionCalled, "Main action callback was called"); isnot(extraMainActionCalled, true, "Extra window's main action callback was not called"); is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed."); is(extraWindow.PanelUI.notificationPanel.state, "closed", "Extra window's update-manual doorhanger is closed."); @@ -77,29 +77,30 @@ add_task(function* testSecondaryActionWo callback: () => { extraMainActionCalled = true; } }; extraWindow.PanelUI.showNotification("update-manual", extraMainAction) isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing."); let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden); is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification."); let doorhanger = notifications[0]; - is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification."); + is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification."); let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton"); secondaryActionButton.click(); is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed."); is(extraWindow.PanelUI.notificationPanel.state, "closed", "Extra window's update-manual doorhanger is closed."); is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is displaying on PanelUI button."); yield PanelUI.show(); isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button."); - let menuItem = doc.getElementById("PanelUI-update-manual-menu-item"); + let menuItem = PanelUI.mainView.querySelector(".panel-banner-item"); + is(menuItem.label, menuItem.getAttribute("label-update-manual"), "Showing correct label"); is(menuItem.hidden, false, "update-manual menu item is showing."); yield PanelUI.hide(); is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is shown on PanelUI button."); yield PanelUI.show(); menuItem.click(); ok(mainActionCalled, "Main action callback was called"); @@ -131,28 +132,29 @@ add_task(function* testInteractionWithBa }; PanelUI.showNotification("update-manual", mainAction); isnot(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is hidden on PanelUI button."); isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing."); let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden); is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification."); let doorhanger = notifications[0]; - is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification."); + is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification."); let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton"); secondaryActionButton.click(); is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed."); is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is displaying on PanelUI button."); yield PanelUI.show(); isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button."); - let menuItem = doc.getElementById("PanelUI-update-manual-menu-item"); + let menuItem = PanelUI.mainView.querySelector(".panel-banner-item"); + is(menuItem.label, menuItem.getAttribute("label-update-manual"), "Showing correct label"); is(menuItem.hidden, false, "update-manual menu item is showing."); menuItem.click(); ok(mainActionCalled, "Main action callback was called"); is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button."); PanelUI.removeNotification(/.*/); is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status"); @@ -174,17 +176,17 @@ add_task(function* testAddingBadgeWhileD PanelUI.showNotification("update-manual", mainAction); PanelUI.showBadgeOnlyNotification("fxa-needs-authentication"); isnot(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is hidden on PanelUI button."); isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing."); let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden); is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification."); let doorhanger = notifications[0]; - is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification."); + is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification."); let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button"); mainActionButton.click(); ok(mainActionCalled, "Main action callback was called"); is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed."); is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button."); PanelUI.removeNotification(/.*/); @@ -260,48 +262,47 @@ add_task(function* testMultipleNonBadges let notifications; let doorhanger; isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing."); notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden); is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification."); doorhanger = notifications[0]; - is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification."); + is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification."); PanelUI.showNotification("update-restart", updateRestartAction); isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing."); notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden); is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification."); doorhanger = notifications[0]; - is(doorhanger.id, "PanelUI-update-restart-notification", "PanelUI is displaying the update-restart notification."); + is(doorhanger.id, "appMenu-update-restart-notification", "PanelUI is displaying the update-restart notification."); let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton"); secondaryActionButton.click(); is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed."); is(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is displaying on PanelUI button."); - let menuItem; - yield PanelUI.show(); isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is hidden on PanelUI button."); - menuItem = doc.getElementById("PanelUI-update-restart-menu-item"); + let menuItem = PanelUI.mainView.querySelector(".panel-banner-item"); + is(menuItem.label, menuItem.getAttribute("label-update-restart"), "Showing correct label"); is(menuItem.hidden, false, "update-restart menu item is showing."); menuItem.click(); ok(updateRestartAction.called, "update-restart main action callback was called"); is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed."); is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "update-manual badge is displaying on PanelUI button."); yield PanelUI.show(); isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "update-manual badge is hidden on PanelUI button."); - menuItem = doc.getElementById("PanelUI-update-manual-menu-item"); + is(menuItem.label, menuItem.getAttribute("label-update-manual"), "Showing correct label"); is(menuItem.hidden, false, "update-manual menu item is showing."); menuItem.click(); ok(updateManualAction.called, "update-manual main action callback was called"); }); }); add_task(function* testFullscreen() { @@ -313,17 +314,17 @@ add_task(function* testFullscreen() { callback: () => { mainActionCalled = true; } }; PanelUI.showNotification("update-manual", mainAction); isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing."); let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden); is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification."); let doorhanger = notifications[0]; - is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification."); + is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification."); let popuphiddenPromise = BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popuphidden"); EventUtils.synthesizeKey("VK_F11", {}); yield popuphiddenPromise; yield new Promise(executeSoon); is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed."); window.FullScreen.showNavToolbox();
--- a/browser/components/preferences/in-content/findInPage.js +++ b/browser/components/preferences/in-content/findInPage.js @@ -198,25 +198,26 @@ var gSearchResultsPane = { gotoPref("paneSearchResults"); this.searchResultsCategory.hidden = false; let resultsFound = false; // Building the range for highlighted areas let rootPreferences = document.getElementById("mainPrefPane") - let rootPreferencesChildren = rootPreferences.children; + let rootPreferencesChildren = rootPreferences + .querySelectorAll(":not([data-hidden-from-search])"); // Showing all the children to bind JS, Access Keys, etc - for (let i = 0; i < rootPreferences.childElementCount; i++) { + for (let i = 0; i < rootPreferencesChildren.length; i++) { rootPreferencesChildren[i].hidden = false; } // Showing or Hiding specific section depending on if words in query are found - for (let i = 0; i < rootPreferences.childElementCount; i++) { + for (let i = 0; i < rootPreferencesChildren.length; i++) { if (rootPreferencesChildren[i].className != "header" && rootPreferencesChildren[i].className != "no-results-message" && this.searchWithinNode(rootPreferencesChildren[i], query)) { rootPreferencesChildren[i].hidden = false; resultsFound = true; } else { rootPreferencesChildren[i].hidden = true; } @@ -290,16 +291,16 @@ var gSearchResultsPane = { valueResult = this.stringMatchesFilters(nodeObject.getAttribute("value"), searchPhrase); } matchesFound = matchesFound || complexTextNodesResult || labelResult || valueResult; } for (let i = 0; i < nodeObject.childNodes.length; i++) { // Search only if child node is not hidden - if (!nodeObject.childNodes[i].hidden) { + if (!nodeObject.childNodes[i].hidden && nodeObject.getAttribute("data-hidden-from-search") !== "true") { let result = this.searchWithinNode(nodeObject.childNodes[i], searchPhrase); matchesFound = matchesFound || result; } } return matchesFound; } }
--- a/browser/components/preferences/in-content/tests/browser_search_within_preferences.js +++ b/browser/components/preferences/in-content/tests/browser_search_within_preferences.js @@ -166,8 +166,40 @@ add_task(function*() { searchInput.value = ""; searchInput.doCommand() // Checks if back to normal is_element_visible(generalPane, "Should be in generalPane"); yield BrowserTestUtils.removeTab(gBrowser.selectedTab); }); + +/** + * Test for "Site Data" case, verifying elements with data-hidden-from-search = true + * are hidden in search result. + */ +add_task(function*() { + yield SpecialPowers.pushPrefEnv({"set": [["browser.storageManager.enabled", false]]}); + yield openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true}); + let generalPane = gBrowser.contentDocument.getElementById("header-general"); + + is_element_hidden(generalPane, "Should not be in general"); + + // Performs search + let searchInput = gBrowser.contentDocument.getElementById("searchInput"); + searchInput.doCommand() + searchInput.value = "site data"; + searchInput.doCommand() + + let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane"); + + let child = mainPrefTag.querySelector("#siteDataGroup"); + is_element_hidden(child, "Should be hidden in search results"); + + // Takes search off + searchInput.value = ""; + searchInput.doCommand() + + // Checks if back to normal + is_element_visible(generalPane, "Should be in generalPane"); + + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); +});
--- a/browser/themes/shared/customizableui/panelUI.inc.css +++ b/browser/themes/shared/customizableui/panelUI.inc.css @@ -119,26 +119,22 @@ border: 1px solid -moz-dialog; /* "!important" is necessary to override the rule in toolbarbutton.css */ margin: -9px 0 0 !important; margin-inline-end: -6px !important; min-width: 16px; min-height: 16px; } -#PanelUI-update-restart-menu-item::after, -#PanelUI-update-available-menu-item::after, -#PanelUI-update-manual-menu-item::after { +.panel-banner-item[notificationid^=update]::after { background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center; border-radius: 50%; } -#PanelUI-update-restart-menu-item, -#PanelUI-update-available-menu-item, -#PanelUI-update-manual-menu-item { +.panel-banner-item[notificationid^=update] { list-style-image: url(chrome://branding/content/icon16.png); } #PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge, #PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge { box-shadow: none; filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15)); } @@ -313,17 +309,17 @@ panelmultiview[nosubviews=true] > .panel padding: 0; } panelview[id^=PanelUI-webext-] { overflow: hidden; } panelview:not([mainview]) .toolbarbutton-text, -.cui-widget-panel toolbarbutton > .toolbarbutton-text { +.cui-widget-panel toolbarbutton:not([wrap]) > .toolbarbutton-text { text-align: start; display: -moz-box; } .cui-widget-panel > .panel-arrowcontainer > .panel-arrowcontent { padding: 4px 0; } @@ -460,17 +456,17 @@ toolbaritem[cui-areatype="menu-panel"][s } #PanelUI-multiView[viewtype="subview"] > .panel-viewcontainer > .panel-viewstack > .panel-mainview > #PanelUI-mainView { background-color: var(--arrowpanel-dimmed); } #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item, #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .toolbarbutton-1:not([panel-multiview-anchor="true"]), -#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > .PanelUI-notification-menu-item, +#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > .panel-banner-item, #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-avatar, #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-label, #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-icon, #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-footer-inner > toolbarseparator, #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-customize, #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-help:not([panel-multiview-anchor="true"]) { opacity: .5; } @@ -582,50 +578,50 @@ toolbarpaletteitem[place="palette"] > to width: 47px; padding-top: 1px; display: block; text-align: center; position: relative; top: 25%; } -#PanelUI-footer-addons > toolbarbutton::after, -.PanelUI-notification-menu-item::after { +.addon-banner-item::after, +.panel-banner-item::after { content: ""; width: 16px; height: 16px; margin-inline-end: 16.5px; display: -moz-box; } -#PanelUI-footer-addons > toolbarbutton { +.addon-banner-item { background-color: #FFEFBF; - /* Force border to override `#PanelUI-footer-addons > toolbarbutton` selector below */ + /* Force border to override `.addon-banner-item` selector below */ border-top: 1px solid hsl(45, 100%, 77%) !important; display: flex; flex: 1 1 0%; width: calc(@menuPanelWidth@ + 30px); padding-inline-start: 15px; border-inline-start-style: none; } -#PanelUI-footer-addons > toolbarbutton:hover { +.addon-banner-item:hover { background-color: #FFE8A2; } -#PanelUI-footer-addons > toolbarbutton:active { +.addon-banner-item:active { background-color: #FFE38F; } -#PanelUI-footer-addons > toolbarbutton > .toolbarbutton-icon { +.addon-banner-item > .toolbarbutton-icon { width: 14px; height: 14px; } -#PanelUI-footer-addons > toolbarbutton::after { +.addon-banner-item::after { background: #FFBF00 url(chrome://browser/skin/update-badge-failed.svg) no-repeat center; } #PanelUI-fxa-status { display: flex; flex: 1 1 0%; width: 1px; } @@ -649,94 +645,103 @@ toolbarpaletteitem[place="palette"] > to -moz-appearance: none; } #PanelUI-footer-inner:hover > toolbarseparator, #PanelUI-footer-fxa:hover > toolbarseparator { margin: 0; } -.PanelUI-notification-menu-item, +.addon-banner-item, +.panel-banner-item, #PanelUI-help, #PanelUI-fxa-label, #PanelUI-fxa-icon, -#PanelUI-footer-addons > toolbarbutton, #PanelUI-customize, #PanelUI-quit { margin: 0; padding: 11px 0; box-sizing: border-box; min-height: 40px; -moz-appearance: none; box-shadow: none; border: none; border-radius: 0; transition: background-color; -moz-box-orient: horizontal; } -.PanelUI-notification-menu-item { +.panel-banner-item { border-top: 1px solid var(--panel-separator-color); border-bottom: 1px solid transparent; margin-bottom: -1px; } -.PanelUI-notification-menu-item > .toolbarbutton-text { +/* in Photon, we have a bottom border as well. Reconcile with the above rule + * after photon launch. */ +#appMenu-mainView > .panel-subview-body > .panel-banner-item { + border-bottom: 1px solid var(--panel-separator-color); + margin-bottom: 3px; + padding-inline-start: 10px; +} + + +.panel-banner-item > .toolbarbutton-text { width: 0; /* Fancy cropping solution for flexbox. */ } #PanelUI-help, #PanelUI-quit { min-width: 46px; } -.PanelUI-notification-menu-item > .toolbarbutton-text, +.addon-banner-item > .toolbarbutton-text, +.panel-banner-item > .toolbarbutton-text, #PanelUI-fxa-label > .toolbarbutton-text, -#PanelUI-footer-addons > toolbarbutton > .toolbarbutton-text, #PanelUI-customize > .toolbarbutton-text { margin: 0; padding: 0 6px; text-align: start; } #PanelUI-help > .toolbarbutton-text, #PanelUI-quit > .toolbarbutton-text, #PanelUI-fxa-avatar > .toolbarbutton-text { display: none; } -.PanelUI-notification-menu-item > .toolbarbutton-icon, +.panel-banner-item > .toolbarbutton-icon, #PanelUI-fxa-label > .toolbarbutton-icon, #PanelUI-fxa-icon > .toolbarbutton-icon, #PanelUI-customize > .toolbarbutton-icon, #PanelUI-help > .toolbarbutton-icon, #PanelUI-quit > .toolbarbutton-icon { margin-inline-end: 0; } #PanelUI-fxa-icon { padding-inline-start: 15px; padding-inline-end: 15px; } #PanelUI-fxa-label, -#PanelUI-footer-addons > toolbarbutton, +.addon-banner-item, #PanelUI-customize { flex: 1; padding-inline-start: 15px; border-inline-start-style: none; } #PanelUI-footer-fxa[fxastatus="signedin"] > #PanelUI-fxa-status > #PanelUI-fxa-label { padding-inline-start: 0px; } /* descend from #PanelUI-footer to add specificity, or else the padding-inline-start will be overridden */ -#PanelUI-footer > .PanelUI-notification-menu-item { +#PanelUI-footer > .panel-banner-item { width: calc(@menuPanelWidth@ + 30px); padding-inline-start: 15px; border-inline-start-style: none; } #PanelUI-fxa-label, #PanelUI-fxa-icon { list-style-image: url(chrome://browser/skin/sync-horizontalbar.png); @@ -873,17 +878,17 @@ toolbarpaletteitem[place="palette"] > to #PanelUI-quit { border-inline-end-style: none; list-style-image: url(chrome://browser/skin/menuPanel-exit.png); } #PanelUI-fxa-label, #PanelUI-fxa-icon, -#PanelUI-footer-addons > toolbarbutton, +.addon-banner-item, #PanelUI-customize, #PanelUI-help, #PanelUI-quit { -moz-image-region: rect(0, 16px, 16px, 0); } #PanelUI-footer-fxa[fxastatus="signedin"] > #PanelUI-fxa-status > #PanelUI-fxa-label > .toolbarbutton-icon, #PanelUI-footer-fxa:not([fxastatus="signedin"]) > #PanelUI-fxa-status > #PanelUI-fxa-avatar { @@ -967,26 +972,26 @@ toolbarpaletteitem[place="palette"] > to } #PanelUI-footer-fxa[fxastatus="login-failed"] > #PanelUI-fxa-status:hover:active, #PanelUI-footer-fxa[fxastatus="unverified"] > #PanelUI-fxa-status:hover:active { background-color: hsl(42,94%,82%); box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset; } -.PanelUI-notification-menu-item { +.panel-banner-item { color: black; background-color: hsla(96,65%,75%,.5); } -.PanelUI-notification-menu-item:not([disabled]):hover { +.panel-banner-item:not([disabled]):hover { background-color: hsla(96,65%,75%,.8); } -.PanelUI-notification-menu-item:not([disabled]):hover:active { +.panel-banner-item:not([disabled]):hover:active { background-color: hsl(96,65%,75%); } #PanelUI-quit:not([disabled]):hover { background-color: #d94141; outline-color: #c23a3a; } @@ -1692,19 +1697,17 @@ menuitem[checked="true"].subviewbutton > } #PanelUI-help[panel-multiview-anchor="true"]:-moz-locale-dir(rtl)::after, toolbarbutton[panel-multiview-anchor="true"]:-moz-locale-dir(rtl) { background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl@2x.png), linear-gradient(rgba(255,255,255,0.3), transparent); } - #PanelUI-update-restart-menu-item, - #PanelUI-update-available-menu-item, - #PanelUI-update-manual-menu-item { + .panel-banner-item[notificationid^=update] { list-style-image: url(chrome://branding/content/icon32.png); } #PanelUI-fxa-label, #PanelUI-fxa-icon { list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png); } @@ -1731,17 +1734,17 @@ menuitem[checked="true"].subviewbutton > #PanelUI-fxa-label, #PanelUI-fxa-icon, #PanelUI-customize, #PanelUI-help, #PanelUI-quit { -moz-image-region: rect(0, 32px, 32px, 0); } - .PanelUI-notification-menu-item > .toolbarbutton-icon, + .panel-banner-item > .toolbarbutton-icon, #PanelUI-fxa-label > .toolbarbutton-icon, #PanelUI-fxa-icon > .toolbarbutton-icon, #PanelUI-customize > .toolbarbutton-icon, #PanelUI-help > .toolbarbutton-icon, #PanelUI-quit > .toolbarbutton-icon { width: 16px; }
--- a/config/external/moz.build +++ b/config/external/moz.build @@ -28,16 +28,19 @@ if CONFIG['MOZ_TREMOR']: external_dirs += ['media/libtremor'] if CONFIG['MOZ_WEBM_ENCODER']: external_dirs += ['media/libmkv'] if not CONFIG['MOZ_SYSTEM_LIBVPX']: external_dirs += ['media/libvpx'] +if CONFIG['MOZ_AV1']: + external_dirs += ['media/libaom'] + if not CONFIG['MOZ_SYSTEM_PNG']: external_dirs += ['media/libpng'] if CONFIG['CPU_ARCH'] == 'arm': external_dirs += ['media/openmax_dl'] if CONFIG['MOZ_WEBSPEECH_POCKETSPHINX']: external_dirs += [
--- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -195,16 +195,17 @@ GK_ATOM(children, "children") GK_ATOM(childList, "childList") GK_ATOM(choose, "choose") GK_ATOM(chromemargin, "chromemargin") GK_ATOM(chromeOnlyContent, "chromeOnlyContent") GK_ATOM(exposeToUntrustedContent, "exposeToUntrustedContent") GK_ATOM(circ, "circ") GK_ATOM(circle, "circle") GK_ATOM(cite, "cite") +GK_ATOM(cjkDecimal, "cjk-decimal") GK_ATOM(_class, "class") GK_ATOM(classid, "classid") GK_ATOM(clear, "clear") GK_ATOM(click, "click") GK_ATOM(clickcount, "clickcount") GK_ATOM(clickthrough, "clickthrough") GK_ATOM(movetoclick, "movetoclick") GK_ATOM(clip, "clip") @@ -268,16 +269,17 @@ GK_ATOM(datalist, "datalist") GK_ATOM(dataType, "data-type") GK_ATOM(dateTime, "date-time") GK_ATOM(datasources, "datasources") GK_ATOM(datetime, "datetime") GK_ATOM(datetimebox, "datetimebox") GK_ATOM(dblclick, "dblclick") GK_ATOM(dd, "dd") GK_ATOM(debug, "debug") +GK_ATOM(decimal, "decimal") GK_ATOM(decimalFormat, "decimal-format") GK_ATOM(decimalSeparator, "decimal-separator") GK_ATOM(deck, "deck") GK_ATOM(declare, "declare") GK_ATOM(decoderDoctor, "decoder-doctor") GK_ATOM(decrement, "decrement") GK_ATOM(_default, "default") GK_ATOM(headerDefaultStyle, "default-style") @@ -557,17 +559,19 @@ GK_ATOM(listitem, "listitem") GK_ATOM(listrows, "listrows") GK_ATOM(load, "load") GK_ATOM(loadingprincipal, "loadingprincipal") GK_ATOM(localedir, "localedir") GK_ATOM(localName, "local-name") GK_ATOM(longdesc, "longdesc") GK_ATOM(loop, "loop") GK_ATOM(low, "low") +GK_ATOM(lowerAlpha, "lower-alpha") GK_ATOM(lowerFirst, "lower-first") +GK_ATOM(lowerRoman, "lower-roman") GK_ATOM(lowest, "lowest") GK_ATOM(lowsrc, "lowsrc") GK_ATOM(ltr, "ltr") GK_ATOM(lwtheme, "lwtheme") GK_ATOM(lwthemetextcolor, "lwthemetextcolor") GK_ATOM(main, "main") GK_ATOM(map, "map") GK_ATOM(manifest, "manifest") @@ -1283,17 +1287,19 @@ GK_ATOM(tv, "tv") GK_ATOM(type, "type") GK_ATOM(typemustmatch, "typemustmatch") GK_ATOM(u, "u") GK_ATOM(ul, "ul") GK_ATOM(underflow, "underflow") GK_ATOM(undetermined, "undetermined") GK_ATOM(unload, "unload") GK_ATOM(unparsedEntityUri, "unparsed-entity-uri") +GK_ATOM(upperAlpha, "upper-alpha") GK_ATOM(upperFirst, "upper-first") +GK_ATOM(upperRoman, "upper-roman") GK_ATOM(uri, "uri") GK_ATOM(use, "use") GK_ATOM(useAttributeSets, "use-attribute-sets") GK_ATOM(usemap, "usemap") GK_ATOM(user_scalable, "user-scalable") GK_ATOM(userInput, "userInput") GK_ATOM(validate, "validate") GK_ATOM(valign, "valign") @@ -1387,16 +1393,17 @@ GK_ATOM(d, "d") GK_ATOM(darken, "darken") GK_ATOM(defs, "defs") GK_ATOM(deg, "deg") GK_ATOM(desc, "desc") GK_ATOM(diffuseConstant, "diffuseConstant") GK_ATOM(dilate, "dilate") GK_ATOM(direction, "direction") GK_ATOM(disable, "disable") +GK_ATOM(disc, "disc") GK_ATOM(discrete, "discrete") GK_ATOM(divisor, "divisor") GK_ATOM(dominant_baseline, "dominant-baseline") GK_ATOM(duplicate, "duplicate") GK_ATOM(dx, "dx") GK_ATOM(dy, "dy") GK_ATOM(edgeMode, "edgeMode") GK_ATOM(ellipse, "ellipse") @@ -2389,8 +2396,272 @@ GK_ATOM(nsuri_svg, "http://www.w3.org/20 GK_ATOM(onsourceopen, "onsourceopen") GK_ATOM(onsourceended, "onsourceended") GK_ATOM(onsourceclosed, "onsourceclosed") GK_ATOM(onupdatestart, "onupdatestart") GK_ATOM(onupdate, "onupdate") GK_ATOM(onupdateend, "onupdateend") GK_ATOM(onaddsourcebuffer, "onaddsourcebuffer") GK_ATOM(onremovesourcebuffer, "onremovesourcebuffer") + +// THE REST OF THE FILE IS GENERATED BY THE HTML PARSER TRANSLATOR AND +// WILL BE OVERWRITTEN! +// Please put manually-added atoms above this section and please avoid #ifdefing +// them so that the translator doesn't need to learn to deal with conditionally +// present manual atoms. +// BEGIN GENERATED +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink, "xlink") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xml_space, "xml:space") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xml_lang, "xml:lang") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xml_base, "xml:base") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(aria_grab, "aria-grab") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(aria_channel, "aria-channel") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(aria_secret, "aria-secret") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(aria_templateid, "aria-templateid") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(aria_datatype, "aria-datatype") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(local, "local") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xchannelselector, "xchannelselector") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(ychannelselector, "ychannelselector") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(enable_background, "enable-background") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(calcmode, "calcmode") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(specularexponent, "specularexponent") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(specularconstant, "specularconstant") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(gradienttransform, "gradienttransform") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(gradientunits, "gradientunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(rendering_intent, "rendering-intent") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(stddeviation, "stddeviation") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(basefrequency, "basefrequency") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(baseprofile, "baseprofile") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(baseProfile, "baseProfile") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(edgemode, "edgemode") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(repeatcount, "repeatcount") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(repeatdur, "repeatdur") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(spreadmethod, "spreadmethod") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(diffuseconstant, "diffuseconstant") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(surfacescale, "surfacescale") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(lengthadjust, "lengthadjust") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(origin, "origin") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(targetx, "targetx") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(targety, "targety") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(pathlength, "pathlength") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(definitionurl, "definitionurl") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(limitingconeangle, "limitingconeangle") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(markerheight, "markerheight") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(markerwidth, "markerwidth") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(maskunits, "maskunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(markerunits, "markerunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(maskcontentunits, "maskcontentunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(tablevalues, "tablevalues") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(primitiveunits, "primitiveunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(zoomandpan, "zoomandpan") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(kernelmatrix, "kernelmatrix") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(kerning, "kerning") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(kernelunitlength, "kernelunitlength") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(pointsatx, "pointsatx") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(pointsaty, "pointsaty") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(pointsatz, "pointsatz") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink_href, "xlink:href") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink_title, "xlink:title") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink_role, "xlink:role") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink_arcrole, "xlink:arcrole") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(arcrole, "arcrole") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xmlns_xlink, "xmlns:xlink") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink_type, "xlink:type") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink_show, "xlink:show") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(xlink_actuate, "xlink:actuate") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(color_rendering, "color-rendering") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(numoctaves, "numoctaves") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(onmousewheel, "onmousewheel") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(clippathunits, "clippathunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(glyph_orientation_vertical, "glyph-orientation-vertical") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(glyph_orientation_horizontal, "glyph-orientation-horizontal") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(glyphref, "glyphref") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(keypoints, "keypoints") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(attributename, "attributename") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(attributetype, "attributetype") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(startoffset, "startoffset") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(keysplines, "keysplines") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(preservealpha, "preservealpha") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(preserveaspectratio, "preserveaspectratio") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(alttext, "alttext") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(filterunits, "filterunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(keytimes, "keytimes") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(patterntransform, "patterntransform") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(patternunits, "patternunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(patterncontentunits, "patterncontentunits") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(stitchtiles, "stitchtiles") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(systemlanguage, "systemlanguage") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(textlength, "textlength") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(requiredfeatures, "requiredfeatures") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(requiredextensions, "requiredextensions") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(viewtarget, "viewtarget") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(viewbox, "viewbox") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(refx, "refx") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(refy, "refy") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(isindex, "isindex") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fefunca, "fefunca") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fefuncb, "fefuncb") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(feblend, "feblend") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(feflood, "feflood") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(feturbulence, "feturbulence") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(femergenode, "femergenode") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(feimage, "feimage") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(femerge, "femerge") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fetile, "fetile") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fecomposite, "fecomposite") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(altglyphdef, "altglyphdef") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(altGlyphDef, "altGlyphDef") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fefuncg, "fefuncg") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fediffuselighting, "fediffuselighting") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fespecularlighting, "fespecularlighting") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(altglyph, "altglyph") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(altGlyph, "altGlyph") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(clippath, "clippath") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(textpath, "textpath") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(altglyphitem, "altglyphitem") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(altGlyphItem, "altGlyphItem") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(animatetransform, "animatetransform") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(animatemotion, "animatemotion") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fedisplacementmap, "fedisplacementmap") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(animatecolor, "animatecolor") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fefuncr, "fefuncr") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fecomponenttransfer, "fecomponenttransfer") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fegaussianblur, "fegaussianblur") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(foreignobject, "foreignobject") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(feoffset, "feoffset") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fespotlight, "fespotlight") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fepointlight, "fepointlight") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fedistantlight, "fedistantlight") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(lineargradient, "lineargradient") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(radialgradient, "radialgradient") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fedropshadow, "fedropshadow") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(fecolormatrix, "fecolormatrix") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(feconvolvematrix, "feconvolvematrix") +// ATOM GENERATED BY HTML PARSER TRANSLATOR (WILL BE AUTOMATICALLY OVERWRITTEN): +GK_ATOM(femorphology, "femorphology") +// END GENERATED ATOMS, DO NOT ADD CODE BELOW THIS LINE
--- a/dom/base/test/test_navigator_hardwareConcurrency.html +++ b/dom/base/test/test_navigator_hardwareConcurrency.html @@ -1,21 +1,39 @@ <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Test for Navigator.hardwareConcurrency</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="application/javascript"> + "use strict"; + + function resistFingerprinting(value) { + return SpecialPowers.pushPrefEnv({"set": [["privacy.resistFingerprinting", value]]}); + } var x = navigator.hardwareConcurrency; is(typeof x, "number", "hardwareConcurrency should be a number."); ok(x > 0, "hardwareConcurrency should be greater than 0."); + SimpleTest.waitForExplicitFinish(); + + resistFingerprinting(true).then(() => { + const y = navigator.hardwareConcurrency; + ok(y === 2, "hardwareConcurrency should always be 2 when we're resisting fingerprinting."); + + resistFingerprinting(false).then(() => { + const z = navigator.hardwareConcurrency; + ok(z === x, "hardwareConcurrency should be the same as before we were resisting fingerprinting."); + SimpleTest.finish(); + }); + }); + </script> </head> <body> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test">
--- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -37,17 +37,16 @@ #include "ProfilerMarkerPayload.h" #endif #include "nsCOMArray.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsDOMCID.h" #include "nsError.h" #include "nsGkAtoms.h" -#include "nsHtml5Atoms.h" #include "nsIContent.h" #include "nsIContentSecurityPolicy.h" #include "nsIDocument.h" #include "nsIDOMEventListener.h" #include "nsIScriptGlobalObject.h" #include "nsISupports.h" #include "nsIXPConnect.h" #include "nsJSUtils.h" @@ -1789,19 +1788,18 @@ bool EventListenerManager::IsApzAwareListener(Listener* aListener) { return !aListener->mFlags.mPassive && IsApzAwareEvent(aListener->mTypeAtom); } bool EventListenerManager::IsApzAwareEvent(nsIAtom* aEvent) { - if (aEvent == nsGkAtoms::onwheel || - aEvent == nsGkAtoms::onDOMMouseScroll || - aEvent == nsHtml5Atoms::onmousewheel || + if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll || + aEvent == nsGkAtoms::onmousewheel || aEvent == nsGkAtoms::onMozMousePixelScroll) { return true; } // In theory we should schedule a repaint if the touch event pref changes, // because the event regions might be out of date. In practice that seems like // overkill because users generally shouldn't be flipping this pref, much // less expecting touch listeners on the page to immediately start preventing // scrolling without so much as a repaint. Tests that we write can work
--- a/dom/filesystem/compat/FileSystemDirectoryReader.h +++ b/dom/filesystem/compat/FileSystemDirectoryReader.h @@ -5,16 +5,17 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_FileSystemDirectoryReader_h #define mozilla_dom_FileSystemDirectoryReader_h #include "mozilla/Attributes.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/FileSystemDirectoryEntry.h" #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" namespace mozilla { namespace dom { class Directory; class FileSystem;
--- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -17,16 +17,17 @@ #include "nsAttrValueInlines.h" #include "nsCRTGlue.h" #include "nsIDOMHTMLInputElement.h" #include "nsITextControlElement.h" #include "nsIDOMNSEditableElement.h" #include "nsIRadioVisitor.h" #include "nsIPhonetic.h" +#include "InputType.h" #include "HTMLFormSubmissionConstants.h" #include "mozilla/Telemetry.h" #include "nsIControllers.h" #include "nsIStringBundle.h" #include "nsFocusManager.h" #include "nsColorControlFrame.h" #include "nsNumberControlFrame.h" @@ -45,17 +46,16 @@ #include "nsIPresShell.h" #include "nsIFormControlFrame.h" #include "nsITextControlFrame.h" #include "nsIFrame.h" #include "nsRangeFrame.h" #include "nsIServiceManager.h" #include "nsError.h" #include "nsIEditor.h" -#include "nsIIOService.h" #include "nsDocument.h" #include "nsAttrValueOrString.h" #include "nsDateTimeControlFrame.h" #include "nsPresState.h" #include "nsIDOMEvent.h" #include "nsIDOMNodeList.h" #include "nsIDOMHTMLCollection.h" @@ -80,18 +80,16 @@ #include "nsIRadioGroupContainer.h" // input type=file #include "mozilla/dom/FileSystemEntry.h" #include "mozilla/dom/FileSystem.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileList.h" #include "nsIFile.h" -#include "nsNetCID.h" -#include "nsNetUtil.h" #include "nsDirectoryServiceDefs.h" #include "nsIContentPrefService.h" #include "nsIMIMEService.h" #include "nsIObserverService.h" #include "nsIPopupWindowManager.h" #include "nsGlobalWindow.h" // input type=image @@ -124,24 +122,16 @@ #include "js/Date.h" NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input) // XXX align=left, hspace, vspace, border? other nav4 attrs static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); -// This must come outside of any namespace, or else it won't overload with the -// double based version in nsMathUtils.h -inline mozilla::Decimal -NS_floorModulo(mozilla::Decimal x, mozilla::Decimal y) -{ - return (x - y * (x / y).floor()); -} - namespace mozilla { namespace dom { // First bits are needed for the control type. #define NS_OUTER_ACTIVATE_EVENT (1 << 9) #define NS_ORIGINAL_CHECKED_VALUE (1 << 10) #define NS_NO_CONTENT_DISPATCH (1 << 11) #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12) @@ -1177,16 +1167,19 @@ HTMLInputElement::HTMLInputElement(alrea static_assert(sizeof(HTMLInputElement) <= 512, "Keep the size of HTMLInputElement under 512 to avoid " "performance regression!"); // We are in a type=text so we now we currenty need a nsTextEditorState. mInputData.mState = nsTextEditorState::Construct(this, &sCachedTextEditorState); + void* memory = mInputTypeMem; + mInputType = InputType::Create(this, mType, memory); + if (!gUploadLastDir) HTMLInputElement::InitUploadLastDir(); // Set up our default state. By default we're enabled (since we're // a control type that can be disabled but not actually disabled // right now), optional, and valid. We are NOT readwrite by default // until someone calls UpdateEditableState on us, apparently! Also // by default we don't have to show validity UI and so forth. @@ -1211,16 +1204,21 @@ HTMLInputElement::FreeData() if (!IsSingleLineTextControl(false)) { free(mInputData.mValue); mInputData.mValue = nullptr; } else { UnbindFromFrame(nullptr); ReleaseTextEditorState(mInputData.mState); mInputData.mState = nullptr; } + + if (mInputType) { + mInputType->DropReference(); + mInputType = nullptr; + } } nsTextEditorState* HTMLInputElement::GetEditorState() const { if (!IsSingleLineTextControl(false)) { return nullptr; } @@ -1452,79 +1450,51 @@ HTMLInputElement::AfterSetAttr(int32_t a if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled || aName == nsGkAtoms::readonly) { UpdateValueMissingValidityState(); // This *has* to be called *after* validity has changed. if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) { UpdateBarredFromConstraintValidation(); } - } else if (MinOrMaxLengthApplies() && aName == nsGkAtoms::maxlength) { + } else if (aName == nsGkAtoms::maxlength) { UpdateTooLongValidityState(); - } else if (MinOrMaxLengthApplies() && aName == nsGkAtoms::minlength) { + } else if (aName == nsGkAtoms::minlength) { UpdateTooShortValidityState(); } else if (aName == nsGkAtoms::pattern && mDoneCreating) { UpdatePatternMismatchValidityState(); } else if (aName == nsGkAtoms::multiple) { UpdateTypeMismatchValidityState(); } else if (aName == nsGkAtoms::max) { UpdateHasRange(); - if (mType == NS_FORM_INPUT_RANGE) { - // The value may need to change when @max changes since the value may - // have been invalid and can now change to a valid value, or vice - // versa. For example, consider: - // <input type=range value=-1 max=1 step=3>. The valid range is 0 to 1 - // while the nearest valid steps are -1 and 2 (the max value having - // prevented there being a valid step in range). Changing @max to/from - // 1 and a number greater than on equal to 3 should change whether we - // have a step mismatch or not. - // The value may also need to change between a value that results in - // a step mismatch and a value that results in overflow. For example, - // if @max in the example above were to change from 1 to -1. - nsAutoString value; - GetNonFileValueInternal(value); - nsresult rv = - SetValueInternal(value, nsTextEditorState::eSetValue_Internal); - NS_ENSURE_SUCCESS(rv, rv); - } - // Validity state must be updated *after* the SetValueInternal call above - // or else the following assert will not be valid. + nsresult rv = mInputType->MinMaxStepAttrChanged(); + NS_ENSURE_SUCCESS(rv, rv); + // Validity state must be updated *after* the UpdateValueDueToAttrChange + // call above or else the following assert will not be valid. // We don't assert the state of underflow during creation since // DoneCreatingElement sanitizes. UpdateRangeOverflowValidityState(); MOZ_ASSERT(!mDoneCreating || mType != NS_FORM_INPUT_RANGE || !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW), "HTML5 spec does not allow underflow for type=range"); } else if (aName == nsGkAtoms::min) { UpdateHasRange(); - if (mType == NS_FORM_INPUT_RANGE) { - // See @max comment - nsAutoString value; - GetNonFileValueInternal(value); - nsresult rv = - SetValueInternal(value, nsTextEditorState::eSetValue_Internal); - NS_ENSURE_SUCCESS(rv, rv); - } + nsresult rv = mInputType->MinMaxStepAttrChanged(); + NS_ENSURE_SUCCESS(rv, rv); // See corresponding @max comment UpdateRangeUnderflowValidityState(); UpdateStepMismatchValidityState(); MOZ_ASSERT(!mDoneCreating || mType != NS_FORM_INPUT_RANGE || !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW), "HTML5 spec does not allow underflow for type=range"); } else if (aName == nsGkAtoms::step) { - if (mType == NS_FORM_INPUT_RANGE) { - // See @max comment - nsAutoString value; - GetNonFileValueInternal(value); - nsresult rv = - SetValueInternal(value, nsTextEditorState::eSetValue_Internal); - NS_ENSURE_SUCCESS(rv, rv); - } + nsresult rv = mInputType->MinMaxStepAttrChanged(); + NS_ENSURE_SUCCESS(rv, rv); // See corresponding @max comment UpdateStepMismatchValidityState(); MOZ_ASSERT(!mDoneCreating || mType != NS_FORM_INPUT_RANGE || !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW), "HTML5 spec does not allow underflow for type=range"); } else if (aName == nsGkAtoms::dir && aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) { @@ -5237,16 +5207,18 @@ HTMLInputElement::HandleTypeChange(uint8 if (GetEditorState()) { mInputData.mState->SyncUpSelectionPropertiesBeforeDestruction(); sp = mInputData.mState->GetSelectionProperties(); } // We already have a copy of the value, lets free it and changes the type. FreeData(); mType = aNewType; + void* memory = mInputTypeMem; + mInputType = InputType::Create(this, mType, memory); if (IsSingleLineTextControl()) { mInputData.mState = nsTextEditorState::Construct(this, &sCachedTextEditorState); if (!sp.IsDefault()) { mInputData.mState->SetSelectionProperties(sp); } @@ -7367,27 +7339,16 @@ HTMLInputElement::PlaceholderApplies() c if (IsDateTimeInputType(mType)) { return false; } return IsSingleLineTextControl(false); } bool -HTMLInputElement::DoesPatternApply() const -{ - // TODO: temporary until bug 773205 is fixed. - if (IsExperimentalMobileType(mType) || IsDateTimeInputType(mType)) { - return false; - } - - return IsSingleLineTextControl(false); -} - -bool HTMLInputElement::DoesMinMaxApply() const { switch (mType) { case NS_FORM_INPUT_NUMBER: case NS_FORM_INPUT_DATE: case NS_FORM_INPUT_TIME: case NS_FORM_INPUT_RANGE: @@ -7504,305 +7465,77 @@ HTMLInputElement::SetCustomValidity(cons return NS_OK; } bool HTMLInputElement::IsTooLong() { if (!mValueChanged || - !mLastValueChangeWasInteractive || - !MinOrMaxLengthApplies() || - !HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength)) { + !mLastValueChangeWasInteractive) { return false; } - int32_t maxLength = MaxLength(); - - // Maxlength of -1 means parsing error. - if (maxLength == -1) { - return false; - } - - return InputTextLength(CallerType::System) > maxLength; + return mInputType->IsTooLong(); } bool HTMLInputElement::IsTooShort() { if (!mValueChanged || - !mLastValueChangeWasInteractive || - !MinOrMaxLengthApplies() || - !HasAttr(kNameSpaceID_None, nsGkAtoms::minlength)) { + !mLastValueChangeWasInteractive) { return false; } - int32_t minLength = MinLength(); - - // Minlength of -1 means parsing error. - if (minLength == -1) { - return false; - } - - int32_t textLength = InputTextLength(CallerType::System); - - return textLength && textLength < minLength; + return mInputType->IsTooShort(); } bool HTMLInputElement::IsValueMissing() const { // Should use UpdateValueMissingValidityStateForRadio() for type radio. MOZ_ASSERT(mType != NS_FORM_INPUT_RADIO); - if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || - !DoesRequiredApply()) { - return false; - } - - if (!IsMutable()) { - return false; - } - - switch (GetValueMode()) { - case VALUE_MODE_VALUE: - return IsValueEmpty(); - - case VALUE_MODE_FILENAME: - return GetFilesOrDirectoriesInternal().IsEmpty(); - - case VALUE_MODE_DEFAULT_ON: - // This should not be used for type radio. - // See the MOZ_ASSERT at the beginning of the method. - return !mChecked; - - case VALUE_MODE_DEFAULT: - default: - return false; - } + return mInputType->IsValueMissing(); } bool HTMLInputElement::HasTypeMismatch() const { - if (mType != NS_FORM_INPUT_EMAIL && mType != NS_FORM_INPUT_URL) { - return false; - } - - nsAutoString value; - GetNonFileValueInternal(value); - - if (value.IsEmpty()) { - return false; - } - - if (mType == NS_FORM_INPUT_EMAIL) { - return HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) - ? !IsValidEmailAddressList(value) : !IsValidEmailAddress(value); - } else if (mType == NS_FORM_INPUT_URL) { - /** - * TODO: - * The URL is not checked as the HTML5 specifications want it to be because - * there is no code to check for a valid URI/IRI according to 3986 and 3987 - * RFC's at the moment, see bug 561586. - * - * RFC 3987 (IRI) implementation: bug 42899 - * - * HTML5 specifications: - * http://dev.w3.org/html5/spec/infrastructure.html#valid-url - */ - nsCOMPtr<nsIIOService> ioService = do_GetIOService(); - nsCOMPtr<nsIURI> uri; - - return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nullptr, - nullptr, getter_AddRefs(uri))); - } - - return false; + return mInputType->HasTypeMismatch(); } bool HTMLInputElement::HasPatternMismatch() const { - if (!DoesPatternApply() || - !HasAttr(kNameSpaceID_None, nsGkAtoms::pattern)) { - return false; - } - - nsAutoString pattern; - GetAttr(kNameSpaceID_None, nsGkAtoms::pattern, pattern); - - nsAutoString value; - GetNonFileValueInternal(value); - - if (value.IsEmpty()) { - return false; - } - - nsIDocument* doc = OwnerDoc(); - - return !nsContentUtils::IsPatternMatching(value, pattern, doc); + return mInputType->HasPatternMismatch(); } bool HTMLInputElement::IsRangeOverflow() const { - if (!DoesMinMaxApply()) { - return false; - } - - Decimal maximum = GetMaximum(); - if (maximum.isNaN()) { - return false; - } - - Decimal value = GetValueAsDecimal(); - if (value.isNaN()) { - return false; - } - - return value > maximum; + return mInputType->IsRangeOverflow(); } bool HTMLInputElement::IsRangeUnderflow() const { - if (!DoesMinMaxApply()) { - return false; - } - - Decimal minimum = GetMinimum(); - if (minimum.isNaN()) { - return false; - } - - Decimal value = GetValueAsDecimal(); - if (value.isNaN()) { - return false; - } - - return value < minimum; + return mInputType->IsRangeUnderflow(); } bool HTMLInputElement::HasStepMismatch(bool aUseZeroIfValueNaN) const { - if (!DoesStepApply()) { - return false; - } - - Decimal value = GetValueAsDecimal(); - if (value.isNaN()) { - if (aUseZeroIfValueNaN) { - value = Decimal(0); - } else { - // The element can't suffer from step mismatch if it's value isn't a number. - return false; - } - } - - Decimal step = GetStep(); - if (step == kStepAny) { - return false; - } - - // Value has to be an integral multiple of step. - return NS_floorModulo(value - GetStepBase(), step) != Decimal(0); -} - -/** - * Takes aEmail and attempts to convert everything after the first "@" - * character (if anything) to punycode before returning the complete result via - * the aEncodedEmail out-param. The aIndexOfAt out-param is set to the index of - * the "@" character. - * - * If no "@" is found in aEmail, aEncodedEmail is simply set to aEmail and - * the aIndexOfAt out-param is set to kNotFound. - * - * Returns true in all cases unless an attempt to punycode encode fails. If - * false is returned, aEncodedEmail has not been set. - * - * This function exists because ConvertUTF8toACE() splits on ".", meaning that - * for 'user.name@sld.tld' it would treat "name@sld" as a label. We want to - * encode the domain part only. - */ -static bool PunycodeEncodeEmailAddress(const nsAString& aEmail, - nsAutoCString& aEncodedEmail, - uint32_t* aIndexOfAt) -{ - nsAutoCString value = NS_ConvertUTF16toUTF8(aEmail); - *aIndexOfAt = (uint32_t)value.FindChar('@'); - - if (*aIndexOfAt == (uint32_t)kNotFound || - *aIndexOfAt == value.Length() - 1) { - aEncodedEmail = value; - return true; - } - - nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID); - if (!idnSrv) { - NS_ERROR("nsIIDNService isn't present!"); - return false; - } - - uint32_t indexOfDomain = *aIndexOfAt + 1; - - const nsDependentCSubstring domain = Substring(value, indexOfDomain); - bool ace; - if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) { - nsAutoCString domainACE; - if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) { - return false; - } - value.Replace(indexOfDomain, domain.Length(), domainACE); - } - - aEncodedEmail = value; - return true; + return mInputType->HasStepMismatch(aUseZeroIfValueNaN); } bool HTMLInputElement::HasBadInput() const { - if (mType == NS_FORM_INPUT_NUMBER) { - nsAutoString value; - GetNonFileValueInternal(value); - if (!value.IsEmpty()) { - // The input can't be bad, otherwise it would have been sanitized to the - // empty string. - NS_ASSERTION(!GetValueAsDecimal().isNaN(), "Should have sanitized"); - return false; - } - nsNumberControlFrame* numberControlFrame = - do_QueryFrame(GetPrimaryFrame()); - if (numberControlFrame && - !numberControlFrame->AnonTextControlIsEmpty()) { - // The input the user entered failed to parse as a number. - return true; - } - return false; - } - if (mType == NS_FORM_INPUT_EMAIL) { - // With regards to suffering from bad input the spec says that only the - // punycode conversion works, so we don't care whether the email address is - // valid or not here. (If the email address is invalid then we will be - // suffering from a type mismatch.) - nsAutoString value; - nsAutoCString unused; - uint32_t unused2; - GetNonFileValueInternal(value); - HTMLSplitOnSpacesTokenizer tokenizer(value, ','); - while (tokenizer.hasMoreTokens()) { - if (!PunycodeEncodeEmailAddress(tokenizer.nextToken(), unused, &unused2)) { - return true; - } - } - return false; - } - return false; + return mInputType->HasBadInput(); } void HTMLInputElement::UpdateTooLongValidityState() { SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong()); } @@ -8187,98 +7920,16 @@ HTMLInputElement::GetValidationMessage(n } default: rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType); } return rv; } -//static -bool -HTMLInputElement::IsValidEmailAddressList(const nsAString& aValue) -{ - HTMLSplitOnSpacesTokenizer tokenizer(aValue, ','); - - while (tokenizer.hasMoreTokens()) { - if (!IsValidEmailAddress(tokenizer.nextToken())) { - return false; - } - } - - return !tokenizer.separatorAfterCurrentToken(); -} - -//static -bool -HTMLInputElement::IsValidEmailAddress(const nsAString& aValue) -{ - // Email addresses can't be empty and can't end with a '.' or '-'. - if (aValue.IsEmpty() || aValue.Last() == '.' || aValue.Last() == '-') { - return false; - } - - uint32_t atPos; - nsAutoCString value; - if (!PunycodeEncodeEmailAddress(aValue, value, &atPos) || - atPos == (uint32_t)kNotFound || atPos == 0 || atPos == value.Length() - 1) { - // Could not encode, or "@" was not found, or it was at the start or end - // of the input - in all cases, not a valid email address. - return false; - } - - uint32_t length = value.Length(); - uint32_t i = 0; - - // Parsing the username. - for (; i < atPos; ++i) { - char16_t c = value[i]; - - // The username characters have to be in this list to be valid. - if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) || - c == '.' || c == '!' || c == '#' || c == '$' || c == '%' || - c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' || - c == '/' || c == '=' || c == '?' || c == '^' || c == '_' || - c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) { - return false; - } - } - - // Skip the '@'. - ++i; - - // The domain name can't begin with a dot or a dash. - if (value[i] == '.' || value[i] == '-') { - return false; - } - - // Parsing the domain name. - for (; i < length; ++i) { - char16_t c = value[i]; - - if (c == '.') { - // A dot can't follow a dot or a dash. - if (value[i-1] == '.' || value[i-1] == '-') { - return false; - } - } else if (c == '-'){ - // A dash can't follow a dot. - if (value[i-1] == '.') { - return false; - } - } else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) || - c == '-')) { - // The domain characters have to be in this list to be valid. - return false; - } - } - - return true; -} - NS_IMETHODIMP_(bool) HTMLInputElement::IsSingleLineTextControl() const { return IsSingleLineTextControl(false); } NS_IMETHODIMP_(bool) HTMLInputElement::IsTextArea() const
--- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -12,27 +12,48 @@ #include "nsImageLoadingContent.h" #include "nsIDOMHTMLInputElement.h" #include "nsITextControlElement.h" #include "nsITimer.h" #include "nsIPhonetic.h" #include "nsIDOMNSEditableElement.h" #include "nsCOMPtr.h" #include "nsIConstraintValidation.h" +#include "mozilla/UniquePtr.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit() #include "mozilla/dom/HTMLInputElementBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/UnionTypes.h" #include "nsIFilePicker.h" #include "nsIContentPrefService2.h" #include "mozilla/Decimal.h" #include "nsContentUtils.h" #include "nsTextEditorState.h" +#include "mozilla/Variant.h" +#include "SingleLineTextInputTypes.h" +#include "NumericInputTypes.h" +#include "CheckableInputTypes.h" +#include "ButtonInputTypes.h" +#include "DateTimeInputTypes.h" +#include "ColorInputType.h" +#include "FileInputType.h" +#include "HiddenInputType.h" +static constexpr size_t INPUT_TYPE_SIZE = sizeof( + mozilla::Variant<TextInputType, SearchInputType, TelInputType, URLInputType, + EmailInputType, PasswordInputType, NumberInputType, + RangeInputType, RadioInputType, CheckboxInputType, + ButtonInputType, ImageInputType, ResetInputType, + SubmitInputType, DateInputType, TimeInputType, WeekInputType, + MonthInputType, DateTimeLocalInputType, FileInputType, + ColorInputType, HiddenInputType> ); + +class InputType; +struct DoNotDelete; class nsIRadioGroupContainer; class nsIRadioVisitor; namespace mozilla { class EventChainPostVisitor; class EventChainPreVisitor; @@ -108,16 +129,17 @@ class HTMLInputElement final : public ns public nsIDOMHTMLInputElement, public nsITextControlElement, public nsIPhonetic, public nsIDOMNSEditableElement, public nsIConstraintValidation { friend class AfterSetFilesOrDirectoriesCallback; friend class DispatchChangeEventCallback; + friend class ::InputType; public: using nsIConstraintValidation::GetValidationMessage; using nsIConstraintValidation::CheckValidity; using nsIConstraintValidation::ReportValidity; using nsIConstraintValidation::WillValidate; using nsIConstraintValidation::Validity; using nsGenericHTMLFormElementWithState::GetForm; @@ -898,38 +920,16 @@ protected: // On getting, returns "C:\fakepath\" followed by the file name of the // first file of the selected files if any. // On setting the empty string, empties the selected files list, otherwise // throw the INVALID_STATE_ERR exception. VALUE_MODE_FILENAME }; /** - * This helper method returns true if aValue is a valid email address. - * This is following the HTML5 specification: - * http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address - * - * @param aValue the email address to check. - * @result whether the given string is a valid email address. - */ - static bool IsValidEmailAddress(const nsAString& aValue); - - /** - * This helper method returns true if aValue is a valid email address list. - * Email address list is a list of email address separated by comas (,) which - * can be surrounded by space charecters. - * This is following the HTML5 specification: - * http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address-list - * - * @param aValue the email address list to check. - * @result whether the given string is a valid email address list. - */ - static bool IsValidEmailAddressList(const nsAString& aValue); - - /** * This helper method convert a sub-string that contains only digits to a * number (unsigned int given that it can't contain a minus sign). * This method will return whether the sub-string is correctly formatted * (ie. contains only digit) and it can be successfuly parsed to generate a * number). * If the method returns true, |aResult| will contained the parsed number. * * @param aValue the string on which the sub-string will be extracted and parsed. @@ -1077,21 +1077,16 @@ protected: bool DoesReadOnlyApply() const; /** * Returns if the required attribute applies for the current type. */ bool DoesRequiredApply() const; /** - * Returns if the pattern attribute applies for the current type. - */ - bool DoesPatternApply() const; - - /** * Returns if the min and max attributes apply for the current type. */ bool DoesMinMaxApply() const; /** * Returns if the step attribute apply for the current type. */ bool DoesStepApply() const { return DoesMinMaxApply(); } @@ -1106,21 +1101,16 @@ protected: */ bool DoesValueAsNumberApply() const { return DoesMinMaxApply(); } /** * Returns if autocomplete attribute applies for the current type. */ bool DoesAutocompleteApply() const; - /** - * Returns if the minlength or maxlength attributes apply for the current type. - */ - bool MinOrMaxLengthApplies() const { return IsSingleLineTextControl(false, mType); } - void FreeData(); nsTextEditorState *GetEditorState() const; /** * Manages the internal data storage across type changes. */ void HandleTypeChange(uint8_t aNewType, bool aNotify); @@ -1562,16 +1552,24 @@ protected: /** * The selection properties cache for number controls. This is needed because * the number controls don't recycle their text field, so the normal cache in * nsTextEditorState cannot do its job. */ nsTextEditorState::SelectionProperties mSelectionProperties; + /* + * InputType object created based on input type. + */ + UniquePtr<InputType, DoNotDelete> mInputType; + + // Memory allocated for mInputType, reused when type changes. + char mInputTypeMem[INPUT_TYPE_SIZE]; + // Step scale factor values, for input types that have one. static const Decimal kStepScaleFactorDate; static const Decimal kStepScaleFactorNumberRange; static const Decimal kStepScaleFactorTime; static const Decimal kStepScaleFactorMonth; static const Decimal kStepScaleFactorWeek; // Default step base value when a type do not have specific one.
new file mode 100644 --- /dev/null +++ b/dom/html/input/ButtonInputTypes.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ButtonInputTypes_h__ +#define ButtonInputTypes_h__ + +#include "InputType.h" + +class ButtonInputTypeBase : public ::InputType +{ +public: + ~ButtonInputTypeBase() override {} + +protected: + explicit ButtonInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} +}; + +// input type=button +class ButtonInputType : public ButtonInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) ButtonInputType(aInputElement); + } + +private: + explicit ButtonInputType(mozilla::dom::HTMLInputElement* aInputElement) + : ButtonInputTypeBase(aInputElement) + {} +}; + +// input type=image +class ImageInputType : public ButtonInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) ImageInputType(aInputElement); + } + +private: + explicit ImageInputType(mozilla::dom::HTMLInputElement* aInputElement) + : ButtonInputTypeBase(aInputElement) + {} +}; + +// input type=reset +class ResetInputType : public ButtonInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) ResetInputType(aInputElement); + } + +private: + explicit ResetInputType(mozilla::dom::HTMLInputElement* aInputElement) + : ButtonInputTypeBase(aInputElement) + {} +}; + +// input type=submit +class SubmitInputType : public ButtonInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) SubmitInputType(aInputElement); + } + +private: + explicit SubmitInputType(mozilla::dom::HTMLInputElement* aInputElement) + : ButtonInputTypeBase(aInputElement) + {} +}; + +#endif /* ButtonInputTypes_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/CheckableInputTypes.cpp @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CheckableInputTypes.h" + +#include "mozilla/dom/HTMLInputElement.h" + +bool +CheckboxInputType::IsValueMissing() const +{ + if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + return false; + } + + if (!IsMutable()) { + return false; + } + + return !mInputElement->Checked(); +}
new file mode 100644 --- /dev/null +++ b/dom/html/input/CheckableInputTypes.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CheckableInputTypes_h__ +#define CheckableInputTypes_h__ + +#include "InputType.h" + +class CheckableInputTypeBase : public ::InputType +{ +public: + ~CheckableInputTypeBase() override {} + +protected: + explicit CheckableInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} +}; + +// input type=checkbox +class CheckboxInputType : public CheckableInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) CheckboxInputType(aInputElement); + } + + bool IsValueMissing() const override; + +private: + explicit CheckboxInputType(mozilla::dom::HTMLInputElement* aInputElement) + : CheckableInputTypeBase(aInputElement) + {} +}; + +// input type=radio +class RadioInputType : public CheckableInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) RadioInputType(aInputElement); + } + +private: + explicit RadioInputType(mozilla::dom::HTMLInputElement* aInputElement) + : CheckableInputTypeBase(aInputElement) + {} +}; + +#endif /* CheckableInputTypes_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/ColorInputType.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ColorInputType_h__ +#define ColorInputType_h__ + +#include "InputType.h" + +// input type=color +class ColorInputType : public ::InputType +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) ColorInputType(aInputElement); + } + +private: + explicit ColorInputType(mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} +}; + +#endif /* ColorInputType_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/DateTimeInputTypes.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "DateTimeInputTypes.h" + +#include "mozilla/dom/HTMLInputElement.h" + +bool +DateTimeInputTypeBase::IsMutable() const +{ + return !mInputElement->IsDisabled() && + !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); +} + +bool +DateTimeInputTypeBase::IsValueMissing() const +{ + if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + return false; + } + + if (!IsMutable()) { + return false; + } + + return IsValueEmpty(); +} + +bool +DateTimeInputTypeBase::IsRangeOverflow() const +{ + mozilla::Decimal maximum = mInputElement->GetMaximum(); + if (maximum.isNaN()) { + return false; + } + + mozilla::Decimal value = mInputElement->GetValueAsDecimal(); + if (value.isNaN()) { + return false; + } + + return value > maximum; +} + +bool +DateTimeInputTypeBase::IsRangeUnderflow() const +{ + mozilla::Decimal minimum = mInputElement->GetMinimum(); + if (minimum.isNaN()) { + return false; + } + + mozilla::Decimal value = mInputElement->GetValueAsDecimal(); + if (value.isNaN()) { + return false; + } + + return value < minimum; +} + +bool +DateTimeInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const +{ + mozilla::Decimal value = mInputElement->GetValueAsDecimal(); + if (value.isNaN()) { + if (aUseZeroIfValueNaN) { + value = mozilla::Decimal(0); + } else { + // The element can't suffer from step mismatch if it's value isn't a number. + return false; + } + } + + mozilla::Decimal step = mInputElement->GetStep(); + if (step == kStepAny) { + return false; + } + + // Value has to be an integral multiple of step. + return NS_floorModulo(value - GetStepBase(), step) != mozilla::Decimal(0); +}
new file mode 100644 --- /dev/null +++ b/dom/html/input/DateTimeInputTypes.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DateTimeInputTypes_h__ +#define DateTimeInputTypes_h__ + +#include "InputType.h" + +class DateTimeInputTypeBase : public ::InputType +{ +public: + ~DateTimeInputTypeBase() override {} + + bool IsValueMissing() const override; + bool IsRangeOverflow() const override; + bool IsRangeUnderflow() const override; + bool HasStepMismatch(bool aUseZeroIfValueNaN) const override; + +protected: + explicit DateTimeInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} + + bool IsMutable() const override; +}; + +// input type=date +class DateInputType : public DateTimeInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) DateInputType(aInputElement); + } + +private: + explicit DateInputType(mozilla::dom::HTMLInputElement* aInputElement) + : DateTimeInputTypeBase(aInputElement) + {} +}; + +// input type=time +class TimeInputType : public DateTimeInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) TimeInputType(aInputElement); + } + +private: + explicit TimeInputType(mozilla::dom::HTMLInputElement* aInputElement) + : DateTimeInputTypeBase(aInputElement) + {} +}; + +// input type=week +class WeekInputType : public DateTimeInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) WeekInputType(aInputElement); + } + +private: + explicit WeekInputType(mozilla::dom::HTMLInputElement* aInputElement) + : DateTimeInputTypeBase(aInputElement) + {} +}; + +// input type=month +class MonthInputType : public DateTimeInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) MonthInputType(aInputElement); + } + +private: + explicit MonthInputType(mozilla::dom::HTMLInputElement* aInputElement) + : DateTimeInputTypeBase(aInputElement) + {} +}; + +// input type=datetime-local +class DateTimeLocalInputType : public DateTimeInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) DateTimeLocalInputType(aInputElement); + } + +private: + explicit DateTimeLocalInputType(mozilla::dom::HTMLInputElement* aInputElement) + : DateTimeInputTypeBase(aInputElement) + {} +}; + + +#endif /* DateTimeInputTypes_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/FileInputType.cpp @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "FileInputType.h" + +#include "mozilla/dom/HTMLInputElement.h" + +bool +FileInputType::IsValueMissing() const +{ + if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + return false; + } + + if (!IsMutable()) { + return false; + } + + return mInputElement->GetFilesOrDirectoriesInternal().IsEmpty(); +}
new file mode 100644 --- /dev/null +++ b/dom/html/input/FileInputType.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef FileInputType_h__ +#define FileInputType_h__ + +#include "InputType.h" + +// input type=file +class FileInputType : public ::InputType +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) FileInputType(aInputElement); + } + + bool IsValueMissing() const override; + +private: + explicit FileInputType(mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} +}; + +#endif /* FileInputType_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/HiddenInputType.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef HiddenInputType_h__ +#define HiddenInputType_h__ + +#include "InputType.h" + +// input type=hidden +class HiddenInputType : public ::InputType +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) HiddenInputType(aInputElement); + } + +private: + explicit HiddenInputType(mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} +}; + +#endif /* HiddenInputType_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/InputType.cpp @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "InputType.h" + +#include "nsIFormControl.h" +#include "ButtonInputTypes.h" +#include "CheckableInputTypes.h" +#include "ColorInputType.h" +#include "DateTimeInputTypes.h" +#include "FileInputType.h" +#include "HiddenInputType.h" +#include "NumericInputTypes.h" +#include "SingleLineTextInputTypes.h" + +const mozilla::Decimal InputType::kStepAny = mozilla::Decimal(0); + +/* static */ mozilla::UniquePtr<InputType, DoNotDelete> +InputType::Create(mozilla::dom::HTMLInputElement* aInputElement, uint8_t aType, + void* aMemory) +{ + mozilla::UniquePtr<InputType, DoNotDelete> inputType; + switch(aType) { + // Single line text + case NS_FORM_INPUT_TEXT: + inputType.reset(TextInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_TEL: + inputType.reset(TelInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_EMAIL: + inputType.reset(EmailInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_SEARCH: + inputType.reset(SearchInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_PASSWORD: + inputType.reset(PasswordInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_URL: + inputType.reset(URLInputType::Create(aInputElement, aMemory)); + break; + // Button + case NS_FORM_INPUT_BUTTON: + inputType.reset(ButtonInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_SUBMIT: + inputType.reset(SubmitInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_IMAGE: + inputType.reset(ImageInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_RESET: + inputType.reset(ResetInputType::Create(aInputElement, aMemory)); + break; + // Checkable + case NS_FORM_INPUT_CHECKBOX: + inputType.reset(CheckboxInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_RADIO: + inputType.reset(RadioInputType::Create(aInputElement, aMemory)); + break; + // Numeric + case NS_FORM_INPUT_NUMBER: + inputType.reset(NumberInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_RANGE: + inputType.reset(RangeInputType::Create(aInputElement, aMemory)); + break; + // DateTime + case NS_FORM_INPUT_DATE: + inputType.reset(DateInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_TIME: + inputType.reset(TimeInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_MONTH: + inputType.reset(MonthInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_WEEK: + inputType.reset(WeekInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_DATETIME_LOCAL: + inputType.reset(DateTimeLocalInputType::Create(aInputElement, aMemory)); + break; + // Others + case NS_FORM_INPUT_COLOR: + inputType.reset(ColorInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_FILE: + inputType.reset(FileInputType::Create(aInputElement, aMemory)); + break; + case NS_FORM_INPUT_HIDDEN: + inputType.reset(HiddenInputType::Create(aInputElement, aMemory)); + break; + default: + inputType.reset(TextInputType::Create(aInputElement, aMemory)); + } + + return inputType; +} + +bool +InputType::IsMutable() const +{ + return !mInputElement->IsDisabled(); +} + +bool +InputType::IsValueEmpty() const +{ + return mInputElement->IsValueEmpty(); +} + +void +InputType::GetNonFileValueInternal(nsAString& aValue) const +{ + return mInputElement->GetNonFileValueInternal(aValue); +} + +nsresult +InputType::SetValueInternal(const nsAString& aValue, uint32_t aFlags) +{ + return mInputElement->SetValueInternal(aValue, aFlags); +} + +mozilla::Decimal +InputType::GetStepBase() const +{ + return mInputElement->GetStepBase(); +} + +nsIFrame* +InputType::GetPrimaryFrame() const +{ + return mInputElement->GetPrimaryFrame(); +} + +void +InputType::DropReference() +{ + // Drop our (non ref-counted) reference. + mInputElement = nullptr; +} + +bool +InputType::IsTooLong() const +{ + return false; +} + +bool +InputType::IsTooShort() const +{ + return false; +} + +bool +InputType::IsValueMissing() const +{ + return false; +} + +bool +InputType::HasTypeMismatch() const +{ + return false; +} + +bool +InputType::HasPatternMismatch() const +{ + return false; +} + +bool +InputType::IsRangeOverflow() const +{ + return false; +} + +bool +InputType::IsRangeUnderflow() const +{ + return false; +} + +bool +InputType::HasStepMismatch(bool aUseZeroIfValueNaN) const +{ + return false; +} + +bool +InputType::HasBadInput() const +{ + return false; +} + +nsresult +InputType::MinMaxStepAttrChanged() +{ + return NS_OK; +}
new file mode 100644 --- /dev/null +++ b/dom/html/input/InputType.h @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef InputType_h__ +#define InputType_h__ + +#include <stdint.h> +#include "mozilla/Decimal.h" +#include "mozilla/UniquePtr.h" +#include "nsString.h" +#include "nsError.h" + +// This must come outside of any namespace, or else it won't overload with the +// double based version in nsMathUtils.h +inline mozilla::Decimal +NS_floorModulo(mozilla::Decimal x, mozilla::Decimal y) +{ + return (x - y * (x / y).floor()); +} + +namespace mozilla { +namespace dom { +class HTMLInputElement; +} // namespace dom +} // namespace mozilla + +struct DoNotDelete; +class nsIFrame; + +/** + * A common superclass for different types of a HTMLInputElement. + */ +class InputType +{ +public: + static mozilla::UniquePtr<InputType, DoNotDelete> + Create(mozilla::dom::HTMLInputElement* aInputElement, uint8_t aType, + void* aMemory); + + virtual ~InputType() {} + + // Float value returned by GetStep() when the step attribute is set to 'any'. + static const mozilla::Decimal kStepAny; + + /** + * Drop the reference to the input element. + */ + void DropReference(); + + virtual bool IsTooLong() const; + virtual bool IsTooShort() const; + virtual bool IsValueMissing() const; + virtual bool HasTypeMismatch() const; + virtual bool HasPatternMismatch() const; + virtual bool IsRangeOverflow() const; + virtual bool IsRangeUnderflow() const; + virtual bool HasStepMismatch(bool aUseZeroIfValueNaN) const; + virtual bool HasBadInput() const; + + virtual nsresult MinMaxStepAttrChanged(); + +protected: + explicit InputType(mozilla::dom::HTMLInputElement* aInputElement) + : mInputElement(aInputElement) + {} + + /** + * Get the mutable state of the element. + * When the element isn't mutable (immutable), the value or checkedness + * should not be changed by the user. + * + * See: https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-fe-mutable + */ + virtual bool IsMutable() const; + + /** + * Returns whether the input element's current value is the empty string. + * This only makes sense for some input types; does NOT make sense for file + * inputs. + * + * @return whether the input element's current value is the empty string. + */ + bool IsValueEmpty() const; + + // A getter for callers that know we're not dealing with a file input, so they + // don't have to think about the caller type. + void GetNonFileValueInternal(nsAString& aValue) const; + + /** + * Setting the input element's value. + * + * @param aValue String to set. + * @param aFlags See nsTextEditorState::SetValueFlags. + */ + nsresult SetValueInternal(const nsAString& aValue, uint32_t aFlags); + + /** + * Return the base used to compute if a value matches step. + * Basically, it's the min attribute if present and a default value otherwise. + * + * @return The step base. + */ + mozilla::Decimal GetStepBase() const; + + /** + * Get the primary frame for the input element. + */ + nsIFrame* GetPrimaryFrame() const; + + mozilla::dom::HTMLInputElement* mInputElement; +}; + +// Custom deleter for UniquePtr<InputType> to avoid freeing memory pre-allocated +// for InputType, but we still need to call the destructor explictly. +struct DoNotDelete { void operator()(::InputType* p) { p->~InputType(); } }; + +#endif /* InputType_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/NumericInputTypes.cpp @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "NumericInputTypes.h" + +#include "mozilla/dom/HTMLInputElement.h" +#include "nsNumberControlFrame.h" +#include "nsTextEditorState.h" + +bool +NumberInputType::IsMutable() const +{ + return !mInputElement->IsDisabled() && + !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); +} + +bool +NumericInputTypeBase::IsRangeOverflow() const +{ + mozilla::Decimal maximum = mInputElement->GetMaximum(); + if (maximum.isNaN()) { + return false; + } + + mozilla::Decimal value = mInputElement->GetValueAsDecimal(); + if (value.isNaN()) { + return false; + } + + return value > maximum; +} + +bool +NumericInputTypeBase::IsRangeUnderflow() const +{ + mozilla::Decimal minimum = mInputElement->GetMinimum(); + if (minimum.isNaN()) { + return false; + } + + mozilla::Decimal value = mInputElement->GetValueAsDecimal(); + if (value.isNaN()) { + return false; + } + + return value < minimum; +} + +bool +NumericInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const +{ + mozilla::Decimal value = mInputElement->GetValueAsDecimal(); + if (value.isNaN()) { + if (aUseZeroIfValueNaN) { + value = mozilla::Decimal(0); + } else { + // The element can't suffer from step mismatch if it's value isn't a number. + return false; + } + } + + mozilla::Decimal step = mInputElement->GetStep(); + if (step == kStepAny) { + return false; + } + + // Value has to be an integral multiple of step. + return NS_floorModulo(value - GetStepBase(), step) != mozilla::Decimal(0); +} + +/* input type=numer */ + +bool +NumberInputType::IsValueMissing() const +{ + if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + return false; + } + + if (!IsMutable()) { + return false; + } + + return IsValueEmpty(); +} + +bool +NumberInputType::HasBadInput() const +{ + nsAutoString value; + GetNonFileValueInternal(value); + if (!value.IsEmpty()) { + // The input can't be bad, otherwise it would have been sanitized to the + // empty string. + NS_ASSERTION(!mInputElement->GetValueAsDecimal().isNaN(), + "Should have sanitized"); + return false; + } + nsNumberControlFrame* numberControlFrame = + do_QueryFrame(GetPrimaryFrame()); + if (numberControlFrame && + !numberControlFrame->AnonTextControlIsEmpty()) { + // The input the user entered failed to parse as a number. + return true; + } + return false; +} + +/* input type=range */ +nsresult +RangeInputType::MinMaxStepAttrChanged() +{ + // The value may need to change when @min/max/step changes since the value may + // have been invalid and can now change to a valid value, or vice versa. For + // example, consider: <input type=range value=-1 max=1 step=3>. The valid + // range is 0 to 1 while the nearest valid steps are -1 and 2 (the max value + // having prevented there being a valid step in range). Changing @max to/from + // 1 and a number greater than on equal to 3 should change whether we have a + // step mismatch or not. + // The value may also need to change between a value that results in a step + // mismatch and a value that results in overflow. For example, if @max in the + // example above were to change from 1 to -1. + nsAutoString value; + GetNonFileValueInternal(value); + return SetValueInternal(value, nsTextEditorState::eSetValue_Internal); +}
new file mode 100644 --- /dev/null +++ b/dom/html/input/NumericInputTypes.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NumericInputTypes_h__ +#define NumericInputTypes_h__ + +#include "InputType.h" + +class NumericInputTypeBase : public ::InputType +{ +public: + ~NumericInputTypeBase() override {} + + bool IsRangeOverflow() const override; + bool IsRangeUnderflow() const override; + bool HasStepMismatch(bool aUseZeroIfValueNaN) const override; + +protected: + explicit NumericInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} +}; + +// input type=number +class NumberInputType : public NumericInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) NumberInputType(aInputElement); + } + + bool IsValueMissing() const override; + bool HasBadInput() const override; + +private: + explicit NumberInputType(mozilla::dom::HTMLInputElement* aInputElement) + : NumericInputTypeBase(aInputElement) + {} + + bool IsMutable() const override; +}; + +// input type=range +class RangeInputType : public NumericInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) RangeInputType(aInputElement); + } + + nsresult MinMaxStepAttrChanged() override; + +private: + explicit RangeInputType(mozilla::dom::HTMLInputElement* aInputElement) + : NumericInputTypeBase(aInputElement) + {} +}; + +#endif /* NumericInputTypes_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/SingleLineTextInputTypes.cpp @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "SingleLineTextInputTypes.h" + +#include "mozilla/dom/HTMLInputElement.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "HTMLSplitOnSpacesTokenizer.h" +#include "nsContentUtils.h" +#include "nsCRTGlue.h" +#include "nsIIDNService.h" +#include "nsIIOService.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" + +bool +SingleLineTextInputTypeBase::IsMutable() const +{ + return !mInputElement->IsDisabled() && + !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); +} + +bool +SingleLineTextInputTypeBase::IsTooLong() const +{ + int32_t maxLength = mInputElement->MaxLength(); + + // Maxlength of -1 means attribute isn't set or parsing error. + if (maxLength == -1) { + return false; + } + + int32_t textLength = + mInputElement->InputTextLength(mozilla::dom::CallerType::System); + + return textLength > maxLength; +} + +bool +SingleLineTextInputTypeBase::IsTooShort() const +{ + int32_t minLength = mInputElement->MinLength(); + + // Minlength of -1 means attribute isn't set or parsing error. + if (minLength == -1) { + return false; + } + + int32_t textLength = + mInputElement->InputTextLength(mozilla::dom::CallerType::System); + + return textLength && textLength < minLength; +} + +bool +SingleLineTextInputTypeBase::IsValueMissing() const +{ + if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + return false; + } + + if (!IsMutable()) { + return false; + } + + return IsValueEmpty(); +} + +bool +SingleLineTextInputTypeBase::HasPatternMismatch() const +{ + nsAutoString pattern; + if (!mInputElement->GetAttr(kNameSpaceID_None, nsGkAtoms::pattern, pattern)) { + return false; + } + + nsAutoString value; + GetNonFileValueInternal(value); + + if (value.IsEmpty()) { + return false; + } + + nsIDocument* doc = mInputElement->OwnerDoc(); + + return !nsContentUtils::IsPatternMatching(value, pattern, doc); +} + +/* input type=url */ + +bool +URLInputType::HasTypeMismatch() const +{ + nsAutoString value; + GetNonFileValueInternal(value); + + if (value.IsEmpty()) { + return false; + } + + /** + * TODO: + * The URL is not checked as the HTML5 specifications want it to be because + * there is no code to check for a valid URI/IRI according to 3986 and 3987 + * RFC's at the moment, see bug 561586. + * + * RFC 3987 (IRI) implementation: bug 42899 + * + * HTML5 specifications: + * http://dev.w3.org/html5/spec/infrastructure.html#valid-url + */ + nsCOMPtr<nsIIOService> ioService = do_GetIOService(); + nsCOMPtr<nsIURI> uri; + + return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nullptr, + nullptr, getter_AddRefs(uri))); + +} + +/* input type=email */ + +bool +EmailInputType::HasTypeMismatch() const +{ + nsAutoString value; + GetNonFileValueInternal(value); + + if (value.IsEmpty()) { + return false; + } + + return mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ? + !IsValidEmailAddressList(value) : !IsValidEmailAddress(value); +} + +bool +EmailInputType::HasBadInput() const +{ + // With regards to suffering from bad input the spec says that only the + // punycode conversion works, so we don't care whether the email address is + // valid or not here. (If the email address is invalid then we will be + // suffering from a type mismatch.) + nsAutoString value; + nsAutoCString unused; + uint32_t unused2; + GetNonFileValueInternal(value); + HTMLSplitOnSpacesTokenizer tokenizer(value, ','); + while (tokenizer.hasMoreTokens()) { + if (!PunycodeEncodeEmailAddress(tokenizer.nextToken(), unused, &unused2)) { + return true; + } + } + return false; +} + +/* static */ bool +EmailInputType::IsValidEmailAddressList(const nsAString& aValue) +{ + HTMLSplitOnSpacesTokenizer tokenizer(aValue, ','); + + while (tokenizer.hasMoreTokens()) { + if (!IsValidEmailAddress(tokenizer.nextToken())) { + return false; + } + } + + return !tokenizer.separatorAfterCurrentToken(); +} + +/* static */ bool +EmailInputType::IsValidEmailAddress(const nsAString& aValue) +{ + // Email addresses can't be empty and can't end with a '.' or '-'. + if (aValue.IsEmpty() || aValue.Last() == '.' || aValue.Last() == '-') { + return false; + } + + uint32_t atPos; + nsAutoCString value; + if (!PunycodeEncodeEmailAddress(aValue, value, &atPos) || + atPos == (uint32_t)kNotFound || atPos == 0 || atPos == value.Length() - 1) { + // Could not encode, or "@" was not found, or it was at the start or end + // of the input - in all cases, not a valid email address. + return false; + } + + uint32_t length = value.Length(); + uint32_t i = 0; + + // Parsing the username. + for (; i < atPos; ++i) { + char16_t c = value[i]; + + // The username characters have to be in this list to be valid. + if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) || + c == '.' || c == '!' || c == '#' || c == '$' || c == '%' || + c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' || + c == '/' || c == '=' || c == '?' || c == '^' || c == '_' || + c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) { + return false; + } + } + + // Skip the '@'. + ++i; + + // The domain name can't begin with a dot or a dash. + if (value[i] == '.' || value[i] == '-') { + return false; + } + + // Parsing the domain name. + for (; i < length; ++i) { + char16_t c = value[i]; + + if (c == '.') { + // A dot can't follow a dot or a dash. + if (value[i-1] == '.' || value[i-1] == '-') { + return false; + } + } else if (c == '-'){ + // A dash can't follow a dot. + if (value[i-1] == '.') { + return false; + } + } else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) || + c == '-')) { + // The domain characters have to be in this list to be valid. + return false; + } + } + + return true; +} + +/* static */ bool +EmailInputType::PunycodeEncodeEmailAddress(const nsAString& aEmail, + nsAutoCString& aEncodedEmail, + uint32_t* aIndexOfAt) +{ + nsAutoCString value = NS_ConvertUTF16toUTF8(aEmail); + *aIndexOfAt = (uint32_t)value.FindChar('@'); + + if (*aIndexOfAt == (uint32_t)kNotFound || + *aIndexOfAt == value.Length() - 1) { + aEncodedEmail = value; + return true; + } + + nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID); + if (!idnSrv) { + NS_ERROR("nsIIDNService isn't present!"); + return false; + } + + uint32_t indexOfDomain = *aIndexOfAt + 1; + + const nsDependentCSubstring domain = Substring(value, indexOfDomain); + bool ace; + if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) { + nsAutoCString domainACE; + if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) { + return false; + } + value.Replace(indexOfDomain, domain.Length(), domainACE); + } + + aEncodedEmail = value; + return true; +}
new file mode 100644 --- /dev/null +++ b/dom/html/input/SingleLineTextInputTypes.h @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef SingleLineTextInputTypes_h__ +#define SingleLineTextInputTypes_h__ + +#include "InputType.h" + +class SingleLineTextInputTypeBase : public ::InputType +{ +public: + ~SingleLineTextInputTypeBase() override {} + + bool IsTooLong() const override; + bool IsTooShort() const override; + bool IsValueMissing() const override; + bool HasPatternMismatch() const override; + +protected: + explicit SingleLineTextInputTypeBase( + mozilla::dom::HTMLInputElement* aInputElement) + : InputType(aInputElement) + {} + + bool IsMutable() const override; +}; + +// input type=text +class TextInputType : public SingleLineTextInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) TextInputType(aInputElement); + } + +private: + explicit TextInputType(mozilla::dom::HTMLInputElement* aInputElement) + : SingleLineTextInputTypeBase(aInputElement) + {} +}; + +// input type=search +class SearchInputType : public SingleLineTextInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) SearchInputType(aInputElement); + } + +private: + explicit SearchInputType(mozilla::dom::HTMLInputElement* aInputElement) + : SingleLineTextInputTypeBase(aInputElement) + {} +}; + +// input type=tel +class TelInputType : public SingleLineTextInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) TelInputType(aInputElement); + } + +private: + explicit TelInputType(mozilla::dom::HTMLInputElement* aInputElement) + : SingleLineTextInputTypeBase(aInputElement) + {} +}; + +// input type=url +class URLInputType : public SingleLineTextInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) URLInputType(aInputElement); + } + + bool HasTypeMismatch() const override; + +private: + explicit URLInputType(mozilla::dom::HTMLInputElement* aInputElement) + : SingleLineTextInputTypeBase(aInputElement) + {} +}; + +// input type=email +class EmailInputType : public SingleLineTextInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) EmailInputType(aInputElement); + } + + bool HasTypeMismatch() const override; + bool HasBadInput() const override; + +private: + explicit EmailInputType(mozilla::dom::HTMLInputElement* aInputElement) + : SingleLineTextInputTypeBase(aInputElement) + {} + + /** + * This helper method returns true if aValue is a valid email address. + * This is following the HTML5 specification: + * http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address + * + * @param aValue the email address to check. + * @result whether the given string is a valid email address. + */ + static bool IsValidEmailAddress(const nsAString& aValue); + + /** + * This helper method returns true if aValue is a valid email address list. + * Email address list is a list of email address separated by comas (,) which + * can be surrounded by space charecters. + * This is following the HTML5 specification: + * http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address-list + * + * @param aValue the email address list to check. + * @result whether the given string is a valid email address list. + */ + static bool IsValidEmailAddressList(const nsAString& aValue); + + /** + * Takes aEmail and attempts to convert everything after the first "@" + * character (if anything) to punycode before returning the complete result + * via the aEncodedEmail out-param. The aIndexOfAt out-param is set to the + * index of the "@" character. + * + * If no "@" is found in aEmail, aEncodedEmail is simply set to aEmail and + * the aIndexOfAt out-param is set to kNotFound. + * + * Returns true in all cases unless an attempt to punycode encode fails. If + * false is returned, aEncodedEmail has not been set. + * + * This function exists because ConvertUTF8toACE() splits on ".", meaning that + * for 'user.name@sld.tld' it would treat "name@sld" as a label. We want to + * encode the domain part only. + */ + static bool PunycodeEncodeEmailAddress(const nsAString& aEmail, + nsAutoCString& aEncodedEmail, + uint32_t* aIndexOfAt); +}; + +// input type=password +class PasswordInputType : public SingleLineTextInputTypeBase +{ +public: + static InputType* + Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) + { + return new (aMemory) PasswordInputType(aInputElement); + } + +private: + explicit PasswordInputType(mozilla::dom::HTMLInputElement* aInputElement) + : SingleLineTextInputTypeBase(aInputElement) + {} +}; + +#endif /* SingleLineTextInputTypes_h__ */
new file mode 100644 --- /dev/null +++ b/dom/html/input/moz.build @@ -0,0 +1,40 @@ +# -*- 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/. + +EXPORTS += [ + 'ButtonInputTypes.h', + 'CheckableInputTypes.h', + 'ColorInputType.h', + 'DateTimeInputTypes.h', + 'FileInputType.h', + 'HiddenInputType.h', + 'InputType.h', + 'NumericInputTypes.h', + 'SingleLineTextInputTypes.h', +] + +UNIFIED_SOURCES += [ + 'CheckableInputTypes.cpp', + 'DateTimeInputTypes.cpp', + 'FileInputType.cpp', + 'InputType.cpp', + 'NumericInputTypes.cpp', + 'SingleLineTextInputTypes.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +LOCAL_INCLUDES += [ + '/dom/base', + '/dom/html', + '/layout/forms', +] + +FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] +
--- a/dom/html/moz.build +++ b/dom/html/moz.build @@ -2,16 +2,18 @@ # 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") +DIRS += ['input'] + MOCHITEST_MANIFESTS += [ 'test/forms/mochitest.ini', 'test/imports/mochitest.ini', 'test/mochitest.ini', ] MOCHITEST_CHROME_MANIFESTS += [ 'test/chrome.ini', @@ -227,16 +229,17 @@ EXTRA_COMPONENTS += [ include('/ipc/chromium/chromium-config.mozbuild') LOCAL_INCLUDES += [ '/caps', '/docshell/base', '/dom/base', '/dom/canvas', + '/dom/html/input', '/dom/media/', '/dom/xbl', '/dom/xul', '/editor/txmgr', '/image', '/layout/forms', '/layout/generic', '/layout/style',
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -152,474 +152,514 @@ using dom::Promise; using dom::Sequence; using media::NewRunnableFrom; using media::NewTaskFrom; using media::Pledge; using media::Refcountable; static Atomic<bool> sInShutdown; +typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid; + static bool HostIsHttps(nsIURI &docURI) { bool isHttps; nsresult rv = docURI.SchemeIs("https", &isHttps); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } return isHttps; } -/** - * This class is an implementation of MediaStreamListener. This is used - * to Start() and Stop() the underlying MediaEngineSource when MediaStreams - * are assigned and deassigned in content. - */ -class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener -{ - friend MediaManager; +class SourceListener : public MediaStreamListener { public: - // Create in an inactive state - GetUserMediaCallbackMediaStreamListener(base::Thread *aThread, - uint64_t aWindowID, - const PrincipalHandle& aPrincipalHandle) - : mMediaThread(aThread) - , mMainThreadCheck(nullptr) - , mWindowID(aWindowID) - , mPrincipalHandle(aPrincipalHandle) - , mStopped(false) - , mFinished(false) - , mRemoved(false) - , mAudioStopped(false) - , mAudioStopPending(false) - , mVideoStopped(false) - , mVideoStopPending(false) - , mChromeNotificationTaskPosted(false) - {} - - ~GetUserMediaCallbackMediaStreamListener() - { - Unused << mMediaThread; - // It's OK to release mStream on any thread; they have thread-safe - // refcounts. - } - - void Activate(already_AddRefed<SourceMediaStream> aStream, + SourceListener(); + + /** + * Registers this source listener as belonging to the given window listener. + */ + void Register(GetUserMediaWindowListener* aListener); + + /** + * Marks this listener as active and adds itself as a listener to aStream. + */ + void Activate(SourceMediaStream* aStream, AudioDevice* aAudioDevice, - VideoDevice* aVideoDevice) - { - MOZ_ASSERT(NS_IsMainThread()); - mMainThreadCheck = PR_GetCurrentThread(); - mStream = aStream; - mAudioDevice = aAudioDevice; - mVideoDevice = aVideoDevice; - - mStream->AddListener(this); - } - - MediaStream *Stream() // Can be used to test if Activate was called + VideoDevice* aVideoDevice); + + /** + * Stops all live tracks, finishes the associated MediaStream and cleans up. + */ + void Stop(); + + /** + * Removes this SourceListener from its associated MediaStream and marks it + * removed. Also removes the weak reference to the associated window listener. + */ + void Remove(); + + /** + * Posts a task to stop the device associated with aTrackID and notifies the + * associated window listener that a track was stopped. + * Should this track be the last live one to be stopped, we'll also clean up. + */ + void StopTrack(TrackID aTrackID); + + /** + * Stops all screen/app/window/audioCapture sharing, but not camera or + * microphone. + */ + void StopSharing(); + + MediaStream* Stream() const { return mStream; } - SourceMediaStream *GetSourceStream() + + SourceMediaStream* GetSourceStream(); + + AudioDevice* GetAudioDevice() const { - NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener"); - if (!mStream) { - return nullptr; - } - return mStream->AsSourceStream(); + return mAudioDevice; + } + + VideoDevice* GetVideoDevice() const + { + return mVideoDevice; } - void StopSharing(); - - void StopTrack(TrackID aID); - - void NotifyChromeOfTrackStops(); - - typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid; + void GetSettings(dom::MediaTrackSettings& aOutSettings, TrackID aTrackID); + + void NotifyPull(MediaStreamGraph* aGraph, + StreamTime aDesiredTime) override; + + void NotifyEvent(MediaStreamGraph* aGraph, + MediaStreamGraphEvent aEvent) override; + + void NotifyFinished(); + + /** + * this can be in response to our own RemoveListener() (via ::Remove()), or + * because the DOM GC'd the DOMLocalMediaStream/etc we're attached to. + */ + void NotifyRemoved(); + + void NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners); + + bool Activated() const + { + return mActivated; + } + + bool Stopped() const + { + return mStopped; + } + + bool CapturingVideo() const; + + bool CapturingAudio() const; + + bool CapturingScreen() const; + + bool CapturingWindow() const; + + bool CapturingApplication() const; + + bool CapturingBrowser() const; already_AddRefed<PledgeVoid> ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow, - TrackID aID, + TrackID aTrackID, const dom::MediaTrackConstraints& aConstraints, dom::CallerType aCallerType); - // mVideo/AudioDevice are set by Activate(), so we assume they're capturing - // if set and represent a real capture device. - bool CapturingVideo() - { - MOZ_ASSERT(NS_IsMainThread()); - return mVideoDevice && !mStopped && - !mVideoDevice->GetSource()->IsAvailable() && - mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera && - (!mVideoDevice->GetSource()->IsFake() || - Preferences::GetBool("media.navigator.permission.fake")); - } - bool CapturingAudio() - { - MOZ_ASSERT(NS_IsMainThread()); - return mAudioDevice && !mStopped && - !mAudioDevice->GetSource()->IsAvailable() && - (!mAudioDevice->GetSource()->IsFake() || - Preferences::GetBool("media.navigator.permission.fake")); - } - bool CapturingScreen() - { - MOZ_ASSERT(NS_IsMainThread()); - return mVideoDevice && !mStopped && - !mVideoDevice->GetSource()->IsAvailable() && - mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen; - } - bool CapturingWindow() - { - MOZ_ASSERT(NS_IsMainThread()); - return mVideoDevice && !mStopped && - !mVideoDevice->GetSource()->IsAvailable() && - mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window; - } - bool CapturingApplication() - { - MOZ_ASSERT(NS_IsMainThread()); - return mVideoDevice && !mStopped && - !mVideoDevice->GetSource()->IsAvailable() && - mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application; - } - bool CapturingBrowser() - { - MOZ_ASSERT(NS_IsMainThread()); - return mVideoDevice && !mStopped && - mVideoDevice->GetSource()->IsAvailable() && - mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser; - } - - void GetSettings(dom::MediaTrackSettings& aOutSettings, TrackID aTrackID) - { - switch (aTrackID) { - case kVideoTrack: - if (mVideoDevice) { - mVideoDevice->GetSource()->GetSettings(aOutSettings); - } - break; - - case kAudioTrack: - if (mAudioDevice) { - mAudioDevice->GetSource()->GetSettings(aOutSettings); - } - break; - } - } - - // implement in .cpp to avoid circular dependency with MediaOperationTask - // Can be invoked from EITHER MainThread or MSG thread - void Stop(); - - void - AudioConfig(bool aEchoOn, uint32_t aEcho, - bool aAgcOn, uint32_t aAGC, - bool aNoiseOn, uint32_t aNoise, - int32_t aPlayoutDelay); - - void - Remove() - { - MOZ_ASSERT(NS_IsMainThread()); - // allow calling even if inactive (!mStream) for easier cleanup - // Caller holds strong reference to us, so no death grip required - if (mStream && !mRemoved) { - MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished)); - mRemoved = true; // RemoveListener is async, avoid races - // If it's destroyed, don't call - listener will be removed and we'll be notified! - if (!mStream->IsDestroyed()) { - mStream->RemoveListener(this); - } - } - } - - // Proxy NotifyPull() to sources - void - NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override - { - // Currently audio sources ignore NotifyPull, but they could - // watch it especially for fake audio. - if (mAudioDevice) { - mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack, - aDesiredTime, mPrincipalHandle); - } - if (mVideoDevice) { - mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack, - aDesiredTime, mPrincipalHandle); - } - } - - void - NotifyEvent(MediaStreamGraph* aGraph, - MediaStreamGraphEvent aEvent) override - { - nsresult rv; - nsCOMPtr<nsIThread> thread; - - switch (aEvent) { - case MediaStreamGraphEvent::EVENT_FINISHED: - rv = NS_GetMainThread(getter_AddRefs(thread)); - if (NS_WARN_IF(NS_FAILED(rv))) { - NS_ASSERTION(false, "Mainthread not available; running on current thread"); - // Ensure this really *was* MainThread (NS_GetCurrentThread won't work) - MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread()); - NotifyFinished(); - return; - } - thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished), - NS_DISPATCH_NORMAL); - break; - case MediaStreamGraphEvent::EVENT_REMOVED: - rv = NS_GetMainThread(getter_AddRefs(thread)); - if (NS_WARN_IF(NS_FAILED(rv))) { - NS_ASSERTION(false, "Mainthread not available; running on current thread"); - // Ensure this really *was* MainThread (NS_GetCurrentThread won't work) - MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread()); - NotifyRemoved(); - return; - } - thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved), - NS_DISPATCH_NORMAL); - break; - case MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS: - NotifyDirectListeners(aGraph, true); - break; - case MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS: - NotifyDirectListeners(aGraph, false); - break; - default: - break; - } - } - - void - NotifyFinished(); - - void - NotifyRemoved(); - - void - NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners); - - PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; } + PrincipalHandle GetPrincipalHandle() const; private: - // Set at construction - base::Thread* mMediaThread; - // never ever indirect off this; just for assertions - PRThread* mMainThreadCheck; - - uint64_t mWindowID; - const PrincipalHandle mPrincipalHandle; - - // true after this listener has sent MEDIA_STOP. MainThread only. + // true after this listener has been Activate()d in a WindowListener. + // MainThread only. + bool mActivated; + + // true after this listener has had all devices stopped. MainThread only. bool mStopped; // true after the stream this listener is listening to has finished in the // MediaStreamGraph. MainThread only. bool mFinished; // true after this listener has been removed from its MediaStream. // MainThread only. bool mRemoved; - // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice. - // MainThread only. + // true if we have stopped mAudioDevice. MainThread only. bool mAudioStopped; - // true if we have scheduled MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice. - // MainThread only. - bool mAudioStopPending; - - // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice. - // MainThread only. + // true if we have stopped mVideoDevice. MainThread only. bool mVideoStopped; - // true if we have scheduled MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice. - // MainThread only. - bool mVideoStopPending; - - // true if we have scheduled a task to notify chrome in the next stable state. - // The task will reset this to false. MainThread only. - bool mChromeNotificationTaskPosted; + // never ever indirect off this; just for assertions + PRThread* mMainThreadCheck; + + // Set in Register() on main thread, then read from any thread. + PrincipalHandle mPrincipalHandle; + + // Weak pointer to the window listener that owns us. MainThread only. + GetUserMediaWindowListener* mWindowListener; // Set at Activate on MainThread // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread // No locking needed as they're only addrefed except on the MediaManager thread RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt RefPtr<SourceMediaStream> mStream; // threadsafe refcnt }; -// Generic class for running long media operations like Start off the main -// thread, and then (because nsDOMMediaStreams aren't threadsafe), -// ProxyReleases mStream since it's cycle collected. -class MediaOperationTask : public Runnable +/** + * This class represents a WindowID and handles all MediaStreamListeners + * (here subclassed as SourceListeners) used to feed GetUserMedia source + * streams. It proxies feedback from them into messages for browser chrome. + * The SourceListeners are used to Start() and Stop() the underlying + * MediaEngineSource when MediaStreams are assigned and deassigned in content. + */ +class GetUserMediaWindowListener { + friend MediaManager; public: - // so we can send Stop without AddRef()ing from the MSG thread - MediaOperationTask(MediaOperation aType, - GetUserMediaCallbackMediaStreamListener* aListener, - DOMMediaStream* aStream, - OnTracksAvailableCallback* aOnTracksAvailableCallback, - AudioDevice* aAudioDevice, - VideoDevice* aVideoDevice, - bool aBool, + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GetUserMediaWindowListener) + + // Create in an inactive state + GetUserMediaWindowListener(base::Thread *aThread, uint64_t aWindowID, - already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError, - const dom::MediaTrackConstraints& aConstraints = dom::MediaTrackConstraints()) - : mType(aType) - , mStream(aStream) - , mOnTracksAvailableCallback(aOnTracksAvailableCallback) - , mAudioDevice(aAudioDevice) - , mVideoDevice(aVideoDevice) - , mListener(aListener) - , mBool(aBool) + const PrincipalHandle& aPrincipalHandle) + : mMediaThread(aThread) , mWindowID(aWindowID) - , mOnFailure(aError) - , mConstraints(aConstraints) + , mPrincipalHandle(aPrincipalHandle) + , mChromeNotificationTaskPosted(false) {} - ~MediaOperationTask() + /** + * Registers an inactive gUM source listener for this WindowListener. + */ + void Register(SourceListener* aListener) { - // MediaStreams can be released on any thread. + MOZ_ASSERT(NS_IsMainThread()); + if (!aListener || aListener->Activated()) { + MOZ_ASSERT(false, "Invalid listener"); + return; + } + if (mInactiveListeners.Contains(aListener)) { + MOZ_ASSERT(false, "Already registered"); + return; + } + if (mActiveListeners.Contains(aListener)) { + MOZ_ASSERT(false, "Already activated"); + return; + } + + aListener->Register(this); + mInactiveListeners.AppendElement(aListener); } - void - ReturnCallbackError(nsresult rv, const char* errorLog); - - NS_IMETHOD - Run() override + /** + * Activates an already registered and inactive gUM source listener for this + * WindowListener. + */ + void Activate(SourceListener* aListener, + SourceMediaStream* aStream, + AudioDevice* aAudioDevice, + VideoDevice* aVideoDevice) + { + MOZ_ASSERT(NS_IsMainThread()); + + if (!aListener || aListener->Activated()) { + MOZ_ASSERT(false, "Cannot activate already activated source listener"); + return; + } + + if (!mInactiveListeners.RemoveElement(aListener)) { + MOZ_ASSERT(false, "Cannot activate non-registered source listener"); + return; + } + + RefPtr<SourceListener> listener = aListener; + listener->Activate(aStream, aAudioDevice, aVideoDevice); + mActiveListeners.AppendElement(listener.forget()); + } + + // Can be invoked from EITHER MainThread or MSG thread + void Stop() { - SourceMediaStream *source = mListener->GetSourceStream(); - // No locking between these is required as all the callbacks for the - // same MediaStream will occur on the same thread. - if (!source) // means the stream was never Activated() - return NS_OK; - - switch (mType) { - case MEDIA_START: - { - NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread"); - nsresult rv; - - if (mAudioDevice) { - rv = mAudioDevice->GetSource()->Start(source, kAudioTrack, - mListener->GetPrincipalHandle()); - if (NS_FAILED(rv)) { - ReturnCallbackError(rv, "Starting audio failed"); - return NS_OK; - } - } - if (mVideoDevice) { - rv = mVideoDevice->GetSource()->Start(source, kVideoTrack, - mListener->GetPrincipalHandle()); - if (NS_FAILED(rv)) { - ReturnCallbackError(rv, "Starting video failed"); - return NS_OK; - } - } - // Start() queued the tracks to be added synchronously to avoid races - source->FinishAddTracks(); - - source->SetPullEnabled(true); - source->AdvanceKnownTracksTime(STREAM_TIME_MAX); - - MM_LOG(("started all sources")); - // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent, - // because mOnTracksAvailableCallback needs to be added to mStream - // on the main thread. - nsIRunnable *event = - new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING, - mStream.forget(), - mOnTracksAvailableCallback.forget(), - mAudioDevice != nullptr, - mVideoDevice != nullptr, - mWindowID, mOnFailure.forget()); - // event must always be released on mainthread due to the JS callbacks - // in the TracksAvailableCallback - NS_DispatchToMainThread(event); - } - break; - - case MEDIA_STOP: - case MEDIA_STOP_TRACK: - { - NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread"); - if (mAudioDevice) { - mAudioDevice->GetSource()->Stop(source, kAudioTrack); - mAudioDevice->Deallocate(); - } - if (mVideoDevice) { - mVideoDevice->GetSource()->Stop(source, kVideoTrack); - mVideoDevice->Deallocate(); - } - if (mType == MEDIA_STOP) { - source->EndAllTrackAndFinish(); - } - - nsIRunnable *event = - new GetUserMediaNotificationEvent(mListener, - mType == MEDIA_STOP ? - GetUserMediaNotificationEvent::STOPPING : - GetUserMediaNotificationEvent::STOPPED_TRACK, - mAudioDevice != nullptr, - mVideoDevice != nullptr, - mWindowID); - // event must always be released on mainthread due to the JS callbacks - // in the TracksAvailableCallback - NS_DispatchToMainThread(event); - } - break; - - case MEDIA_DIRECT_LISTENERS: - { - NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread"); - if (mVideoDevice) { - mVideoDevice->GetSource()->SetDirectListeners(mBool); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread"); + + for (auto& source : mActiveListeners) { + source->Stop(); + } + + // Once all tracks have stopped, that will trigger the chrome notification + } + + /** + * Removes all SourceListeners from this window listener. + * Removes this window listener from the list of active windows, so callers + * need to make sure to hold a strong reference. + */ + void RemoveAll() + { + MOZ_ASSERT(NS_IsMainThread()); + + // Shallow copy since SourceListener::Remove() will modify the arrays. + nsTArray<RefPtr<SourceListener>> listeners(mInactiveListeners.Length() + + mActiveListeners.Length()); + listeners.AppendElements(mInactiveListeners); + listeners.AppendElements(mActiveListeners); + for (auto& l : listeners) { + Remove(l); + } + + MOZ_ASSERT(mInactiveListeners.Length() == 0); + MOZ_ASSERT(mActiveListeners.Length() == 0); + + RefPtr<MediaManager> mgr = MediaManager::GetIfExists(); + if (!mgr) { + MOZ_ASSERT(false, "MediaManager should stay until everything is removed"); + return; + } + GetUserMediaWindowListener* windowListener = + mgr->GetWindowListener(mWindowID); + + if (!windowListener) { + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID); + if (globalWindow) { + RefPtr<GetUserMediaRequest> req = + new GetUserMediaRequest(globalWindow->AsInner(), + NullString(), NullString()); + obs->NotifyObservers(req, "recording-device-stopped", nullptr); + } + return; + } + + MOZ_ASSERT(windowListener == this, + "There should only be one window listener per window ID"); + + LOG(("GUMWindowListener %p removing windowID %" PRIu64, this, mWindowID)); + mgr->RemoveWindowID(mWindowID); + } + + bool Remove(SourceListener* aListener) + { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mInactiveListeners.RemoveElement(aListener) && + !mActiveListeners.RemoveElement(aListener)) { + return false; + } + + MOZ_ASSERT(!mInactiveListeners.Contains(aListener), + "A SourceListener should only be once in one of " + "mInactiveListeners and mActiveListeners"); + MOZ_ASSERT(!mActiveListeners.Contains(aListener), + "A SourceListener should only be once in one of " + "mInactiveListeners and mActiveListeners"); + + LOG(("GUMWindowListener %p removing SourceListener %p.", this, aListener)); + aListener->Remove(); + + if (VideoDevice* removedDevice = aListener->GetVideoDevice()) { + bool revokeVideoPermission = true; + nsString removedRawId; + nsString removedSourceType; + removedDevice->GetRawId(removedRawId); + removedDevice->GetMediaSource(removedSourceType); + for (const auto& l : mActiveListeners) { + if (VideoDevice* device = l->GetVideoDevice()) { + nsString rawId; + device->GetRawId(rawId); + if (removedRawId.Equals(rawId)) { + revokeVideoPermission = false; + break; } } - break; - - default: - MOZ_ASSERT(false,"invalid MediaManager operation"); - break; + } + + if (revokeVideoPermission) { + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID); + nsPIDOMWindowInner* window = globalWindow ? globalWindow->AsInner() + : nullptr; + RefPtr<GetUserMediaRequest> req = + new GetUserMediaRequest(window, removedRawId, removedSourceType); + obs->NotifyObservers(req, "recording-device-stopped", nullptr); + } + } + + if (AudioDevice* removedDevice = aListener->GetAudioDevice()) { + bool revokeAudioPermission = true; + nsString removedRawId; + nsString removedSourceType; + removedDevice->GetRawId(removedRawId); + removedDevice->GetMediaSource(removedSourceType); + for (const auto& l : mActiveListeners) { + if (AudioDevice* device = l->GetAudioDevice()) { + nsString rawId; + device->GetRawId(rawId); + if (removedRawId.Equals(rawId)) { + revokeAudioPermission = false; + break; + } + } + } + + if (revokeAudioPermission) { + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID); + nsPIDOMWindowInner* window = globalWindow ? globalWindow->AsInner() + : nullptr; + RefPtr<GetUserMediaRequest> req = + new GetUserMediaRequest(window, removedRawId, removedSourceType); + obs->NotifyObservers(req, "recording-device-stopped", nullptr); + } + } + + if (mInactiveListeners.Length() == 0 && + mActiveListeners.Length() == 0) { + LOG(("GUMWindowListener %p Removed the last SourceListener. " + "Cleaning up.", this)); + RemoveAll(); } - return NS_OK; + return true; + } + + void StopSharing(); + + /** + * Called by one of our SourceListeners when one of its tracks has stopped. + * Schedules an event for the next stable state to update chrome. + */ + void NotifySourceTrackStopped(); + + /** + * Called in stable state to send a notification to update chrome. + */ + void NotifyChromeOfTrackStops(); + + bool CapturingVideo() const + { + MOZ_ASSERT(NS_IsMainThread()); + for (auto& l : mActiveListeners) { + if (l->CapturingVideo()) { + return true; + } + } + return false; + } + bool CapturingAudio() const + { + MOZ_ASSERT(NS_IsMainThread()); + for (auto& l : mActiveListeners) { + if (l->CapturingAudio()) { + return true; + } + } + return false; } + bool CapturingScreen() const + { + MOZ_ASSERT(NS_IsMainThread()); + for (auto& l : mActiveListeners) { + if (l->CapturingScreen()) { + return true; + } + } + return false; + } + bool CapturingWindow() const + { + MOZ_ASSERT(NS_IsMainThread()); + for (auto& l : mActiveListeners) { + if (l->CapturingWindow()) { + return true; + } + } + return false; + } + bool CapturingApplication() const + { + MOZ_ASSERT(NS_IsMainThread()); + for (auto& l : mActiveListeners) { + if (l->CapturingApplication()) { + return true; + } + } + return false; + } + bool CapturingBrowser() const + { + MOZ_ASSERT(NS_IsMainThread()); + for (auto& l : mActiveListeners) { + if (l->CapturingBrowser()) { + return true; + } + } + return false; + } + + uint64_t WindowID() const + { + return mWindowID; + } + + PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; } private: - MediaOperation mType; - RefPtr<DOMMediaStream> mStream; - nsAutoPtr<OnTracksAvailableCallback> mOnTracksAvailableCallback; - RefPtr<AudioDevice> mAudioDevice; // threadsafe - RefPtr<VideoDevice> mVideoDevice; // threadsafe - RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe - bool mBool; + ~GetUserMediaWindowListener() + { + for (auto& l : mInactiveListeners) { + l->NotifyRemoved(); + } + mInactiveListeners.Clear(); + for (auto& l : mActiveListeners) { + l->NotifyRemoved(); + } + mActiveListeners.Clear(); + Unused << mMediaThread; + // It's OK to release mStream on any thread; they have thread-safe + // refcounts. + } + + // Set at construction + base::Thread* mMediaThread; + uint64_t mWindowID; - nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure; - dom::MediaTrackConstraints mConstraints; + const PrincipalHandle mPrincipalHandle; + + // true if we have scheduled a task to notify chrome in the next stable state. + // The task will reset this to false. MainThread only. + bool mChromeNotificationTaskPosted; + + nsTArray<RefPtr<SourceListener>> mInactiveListeners; + nsTArray<RefPtr<SourceListener>> mActiveListeners; }; /** * Send an error back to content. * Do this only on the main thread. The onSuccess callback is also passed here * so it can be released correctly. */ template<class SuccessCallbackType> class ErrorCallbackRunnable : public Runnable { public: ErrorCallbackRunnable( - nsCOMPtr<SuccessCallbackType>& aOnSuccess, - nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure, + nsCOMPtr<SuccessCallbackType>&& aOnSuccess, + nsCOMPtr<nsIDOMGetUserMediaErrorCallback>&& aOnFailure, MediaMgrError& aError, uint64_t aWindowID) : mError(&aError) , mWindowID(aWindowID) , mManager(MediaManager::GetInstance()) { mOnSuccess.swap(aOnSuccess); mOnFailure.swap(aOnFailure); @@ -654,39 +694,16 @@ private: nsCOMPtr<SuccessCallbackType> mOnSuccess; nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure; RefPtr<MediaMgrError> mError; uint64_t mWindowID; RefPtr<MediaManager> mManager; // get ref to this when creating the runnable }; -// Handle removing GetUserMediaCallbackMediaStreamListener from main thread -class GetUserMediaListenerRemove: public Runnable -{ -public: - GetUserMediaListenerRemove(uint64_t aWindowID, - GetUserMediaCallbackMediaStreamListener *aListener) - : mWindowID(aWindowID) - , mListener(aListener) {} - - NS_IMETHOD - Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - RefPtr<MediaManager> manager(MediaManager::GetInstance()); - manager->RemoveFromWindowList(mWindowID, mListener); - return NS_OK; - } - -protected: - uint64_t mWindowID; - RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; -}; - /** * nsIMediaDevice implementation. */ NS_IMPL_ISUPPORTS(MediaDevice, nsIMediaDevice) MediaDevice::MediaDevice(MediaEngineSource* aSource, bool aIsVideo) : mScary(aSource->GetScary()) , mMediaSource(aSource->GetMediaSource()) @@ -889,35 +906,16 @@ nsresult MediaDevice::Restart(const dom: return GetSource()->Restart(mAllocationHandle, aConstraints, aPrefs, mID, aOutBadConstraint); } nsresult MediaDevice::Deallocate() { return GetSource()->Deallocate(mAllocationHandle); } -void -MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog) -{ - MM_LOG(("%s , rv=%" PRIu32, errorLog, static_cast<uint32_t>(rv))); - NS_DispatchToMainThread(do_AddRef(new ReleaseMediaOperationResource(mStream.forget(), - mOnTracksAvailableCallback.forget()))); - nsString log; - - log.AssignASCII(errorLog); - nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess; - RefPtr<MediaMgrError> error = new MediaMgrError( - NS_LITERAL_STRING("InternalError"), log); - NS_DispatchToMainThread(do_AddRef( - new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(onSuccess, - mOnFailure, - *error, - mWindowID))); -} - static bool IsOn(const OwningBooleanOrMediaTrackConstraints &aUnion) { return !aUnion.IsBoolean() || aUnion.GetAsBoolean(); } static const MediaTrackConstraints& GetInvariant(const OwningBooleanOrMediaTrackConstraints &aUnion) { static const MediaTrackConstraints empty; @@ -982,42 +980,44 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(FakeT */ class GetUserMediaStreamRunnable : public Runnable { public: GetUserMediaStreamRunnable( nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess, nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure, uint64_t aWindowID, - GetUserMediaCallbackMediaStreamListener* aListener, + GetUserMediaWindowListener* aWindowListener, + SourceListener* aSourceListener, const ipc::PrincipalInfo& aPrincipalInfo, const MediaStreamConstraints& aConstraints, AudioDevice* aAudioDevice, VideoDevice* aVideoDevice, PeerIdentity* aPeerIdentity) : mConstraints(aConstraints) , mAudioDevice(aAudioDevice) , mVideoDevice(aVideoDevice) , mWindowID(aWindowID) - , mListener(aListener) + , mWindowListener(aWindowListener) + , mSourceListener(aSourceListener) , mPrincipalInfo(aPrincipalInfo) , mPeerIdentity(aPeerIdentity) , mManager(MediaManager::GetInstance()) { mOnSuccess.swap(aOnSuccess); mOnFailure.swap(aOnFailure); } ~GetUserMediaStreamRunnable() {} class TracksAvailableCallback : public OnTracksAvailableCallback { public: TracksAvailableCallback(MediaManager* aManager, - nsIDOMGetUserMediaSuccessCallback* aSuccess, + already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess, uint64_t aWindowID, DOMMediaStream* aStream) : mWindowID(aWindowID), mOnSuccess(aSuccess), mManager(aManager), mStream(aStream) {} void NotifyTracksAvailable(DOMMediaStream* aStream) override { // We're in the main thread, so no worries here. if (!(mManager->IsWindowStillActive(mWindowID))) { @@ -1051,18 +1051,19 @@ public: Run() override { MOZ_ASSERT(NS_IsMainThread()); nsGlobalWindow* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID); nsPIDOMWindowInner* window = globalWindow ? globalWindow->AsInner() : nullptr; // We're on main-thread, and the windowlist can only // be invalidated from the main-thread (see OnNavigation) - StreamListeners* listeners = mManager->GetWindowListeners(mWindowID); - if (!listeners || !window || !window->GetExtantDoc()) { + GetUserMediaWindowListener* listener = + mManager->GetWindowListener(mWindowID); + if (!listener || !window || !window->GetExtantDoc()) { // This window is no longer live. mListener has already been removed return NS_OK; } MediaStreamGraph::GraphDriverType graphDriverType = mAudioDevice ? MediaStreamGraph::AUDIO_THREAD_DRIVER : MediaStreamGraph::SYSTEM_THREAD_DRIVER; MediaStreamGraph* msg = @@ -1089,33 +1090,34 @@ public: mWindowID, domStream->GetInputStream()->AsProcessedStream()); window->SetAudioCapture(true); } else { class LocalTrackSource : public MediaStreamTrackSource { public: LocalTrackSource(nsIPrincipal* aPrincipal, const nsString& aLabel, - GetUserMediaCallbackMediaStreamListener* aListener, + SourceListener* aListener, const MediaSourceEnum aSource, const TrackID aTrackID, const PeerIdentity* aPeerIdentity) : MediaStreamTrackSource(aPrincipal, aLabel), mListener(aListener), mSource(aSource), mTrackID(aTrackID), mPeerIdentity(aPeerIdentity) {} MediaSourceEnum GetMediaSource() const override { return mSource; } const PeerIdentity* GetPeerIdentity() const override { return mPeerIdentity; } + already_AddRefed<PledgeVoid> ApplyConstraints(nsPIDOMWindowInner* aWindow, const MediaTrackConstraints& aConstraints, dom::CallerType aCallerType) override { if (sInShutdown || !mListener) { // Track has been stopped, or we are in shutdown. In either case // there's no observable outcome, so pretend we succeeded. @@ -1141,17 +1143,17 @@ public: mListener->StopTrack(mTrackID); mListener = nullptr; } } protected: ~LocalTrackSource() {} - RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; + RefPtr<SourceListener> mListener; const MediaSourceEnum mSource; const TrackID mTrackID; const RefPtr<const PeerIdentity> mPeerIdentity; }; nsCOMPtr<nsIPrincipal> principal; if (mPeerIdentity) { principal = NullPrincipal::CreateWithInheritedAttributes(window->GetExtantDoc()->NodePrincipal()); @@ -1160,85 +1162,142 @@ public: } // Normal case, connect the source stream to the track union stream to // avoid us blocking. Pass a simple TrackSourceGetter for potential // fake tracks. Apart from them gUM never adds tracks dynamically. domStream = DOMLocalMediaStream::CreateSourceStreamAsInput(window, msg, new FakeTrackSourceGetter(principal)); + stream = domStream->GetInputStream()->AsSourceStream(); if (mAudioDevice) { nsString audioDeviceName; mAudioDevice->GetName(audioDeviceName); const MediaSourceEnum source = mAudioDevice->GetSource()->GetMediaSource(); RefPtr<MediaStreamTrackSource> audioSource = - new LocalTrackSource(principal, audioDeviceName, mListener, source, - kAudioTrack, mPeerIdentity); + new LocalTrackSource(principal, audioDeviceName, mSourceListener, + source, kAudioTrack, mPeerIdentity); MOZ_ASSERT(IsOn(mConstraints.mAudio)); RefPtr<MediaStreamTrack> track = domStream->CreateDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioSource, GetInvariant(mConstraints.mAudio)); domStream->AddTrackInternal(track); } if (mVideoDevice) { nsString videoDeviceName; mVideoDevice->GetName(videoDeviceName); const MediaSourceEnum source = mVideoDevice->GetSource()->GetMediaSource(); RefPtr<MediaStreamTrackSource> videoSource = - new LocalTrackSource(principal, videoDeviceName, mListener, source, - kVideoTrack, mPeerIdentity); + new LocalTrackSource(principal, videoDeviceName, mSourceListener, + source, kVideoTrack, mPeerIdentity); MOZ_ASSERT(IsOn(mConstraints.mVideo)); RefPtr<MediaStreamTrack> track = domStream->CreateDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoSource, GetInvariant(mConstraints.mVideo)); domStream->AddTrackInternal(track); } - stream = domStream->GetInputStream()->AsSourceStream(); } - if (!domStream || sInShutdown) { + if (!domStream || !stream || sInShutdown) { nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget(); LOG(("Returning error for getUserMedia() - no stream")); if (auto* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID)) { RefPtr<MediaStreamError> error = new MediaStreamError(window->AsInner(), NS_LITERAL_STRING("InternalError"), sInShutdown ? NS_LITERAL_STRING("In shutdown") : NS_LITERAL_STRING("No stream.")); onFailure->OnError(error); } return NS_OK; } - // The listener was added at the beginning in an inactive state. - // Activate our listener. We'll call Start() on the source when get a callback - // that the MediaStream has started consuming. The listener is freed - // when the page is invalidated (on navigation or close). - MOZ_ASSERT(stream); - mListener->Activate(stream.forget(), mAudioDevice, mVideoDevice); + // Activate our source listener. We'll call Start() on the source when we + // get a callback that the MediaStream has started consuming. The listener + // is freed when the page is invalidated (on navigation or close). + mWindowListener->Activate(mSourceListener, stream, mAudioDevice, mVideoDevice); // Note: includes JS callbacks; must be released on MainThread - TracksAvailableCallback* tracksAvailableCallback = - new TracksAvailableCallback(mManager, mOnSuccess, mWindowID, domStream); + auto callback = MakeRefPtr<Refcountable<UniquePtr<OnTracksAvailableCallback>>>( + new TracksAvailableCallback(mManager, mOnSuccess.forget(), mWindowID, domStream)); // Dispatch to the media thread to ask it to start the sources, // because that can take a while. - // Pass ownership of domStream to the MediaOperationTask - // to ensure it's kept alive until the MediaOperationTask runs (at least). - RefPtr<Runnable> mediaOperation = - new MediaOperationTask(MEDIA_START, mListener, domStream, - tracksAvailableCallback, - mAudioDevice, mVideoDevice, - false, mWindowID, mOnFailure.forget()); - MediaManager::PostTask(mediaOperation.forget()); - // We won't need mOnFailure now. - mOnFailure = nullptr; + // Pass ownership of domStream through the lambda to + // GetUserMediaNotificationEvent to ensure it's kept alive until the + // GetUserMediaNotificationEvent runs or is discarded. + RefPtr<GetUserMediaStreamRunnable> self = this; + MediaManager::PostTask(NewTaskFrom([self, domStream, callback]() mutable { + MOZ_ASSERT(MediaManager::IsInMediaThread()); + SourceMediaStream* source = self->mSourceListener->GetSourceStream(); + + RefPtr<MediaMgrError> error = nullptr; + if (self->mAudioDevice) { + nsresult rv = + self->mAudioDevice->GetSource()->Start(source, kAudioTrack, + self->mSourceListener->GetPrincipalHandle()); + if (NS_FAILED(rv)) { + nsString log; + log.AssignASCII("Starting audio failed"); + error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log); + } + } + + if (!error && self->mVideoDevice) { + nsresult rv = + self->mVideoDevice->GetSource()->Start(source, kVideoTrack, + self->mSourceListener->GetPrincipalHandle()); + if (NS_FAILED(rv)) { + nsString log; + log.AssignASCII("Starting video failed"); + error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log); + } + } + + if (error) { + // The DOM stream and track callback must be released on main thread. + NS_DispatchToMainThread(do_AddRef(new ReleaseMediaOperationResource( + domStream.forget(), callback.forget()))); + + // Dispatch the error callback on main thread. + nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess; + NS_DispatchToMainThread(do_AddRef( + new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>( + Move(onSuccess), Move(self->mOnFailure), *error, self->mWindowID))); + + // This should be empty now + MOZ_ASSERT(!self->mOnFailure); + return NS_OK; + } + + // Start() queued the tracks to be added synchronously to avoid races + source->FinishAddTracks(); + + source->SetPullEnabled(true); + source->AdvanceKnownTracksTime(STREAM_TIME_MAX); + + LOG(("started all sources")); + + // Forward onTracksAvailableCallback to GetUserMediaNotificationEvent, + // because onTracksAvailableCallback needs to be added to domStream + // on the main thread. + // The event runnable must always be released on mainthread due to the JS + // callbacks in the TracksAvailableCallback. + NS_DispatchToMainThread(do_AddRef( + new GetUserMediaNotificationEvent( + GetUserMediaNotificationEvent::STARTING, + domStream.forget(), + callback.forget(), + self->mWindowID, + self->mOnFailure.forget()))); + return NS_OK; + })); if (!IsPincipalInfoPrivate(mPrincipalInfo)) { // Call GetPrincipalKey again, this time w/persist = true, to promote // deviceIds to persistent, in case they're not already. Fire'n'forget. RefPtr<Pledge<nsCString>> p = media::GetPrincipalKey(mPrincipalInfo, true); } return NS_OK; @@ -1246,17 +1305,18 @@ public: private: nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess; nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure; MediaStreamConstraints mConstraints; RefPtr<AudioDevice> mAudioDevice; RefPtr<VideoDevice> mVideoDevice; uint64_t mWindowID; - RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; + RefPtr<GetUserMediaWindowListener> mWindowListener; + RefPtr<SourceListener> mSourceListener; ipc::PrincipalInfo mPrincipalInfo; RefPtr<PeerIdentity> mPeerIdentity; RefPtr<MediaManager> mManager; // get ref to this when creating the runnable }; // Source getter returning full list template<class DeviceType> @@ -1379,51 +1439,53 @@ MediaManager::SelectSettings( */ class GetUserMediaTask : public Runnable { public: GetUserMediaTask( const MediaStreamConstraints& aConstraints, already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aOnSuccess, already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure, - uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener, - MediaEnginePrefs &aPrefs, + uint64_t aWindowID, GetUserMediaWindowListener *aWindowListener, + SourceListener *aSourceListener, MediaEnginePrefs &aPrefs, const ipc::PrincipalInfo& aPrincipalInfo, bool aIsChrome, MediaManager::SourceSet* aSourceSet) : mConstraints(aConstraints) , mOnSuccess(aOnSuccess) , mOnFailure(aOnFailure) , mWindowID(aWindowID) - , mListener(aListener) + , mWindowListener(aWindowListener) + , mSourceListener(aSourceListener) , mPrefs(aPrefs) , mPrincipalInfo(aPrincipalInfo) , mIsChrome(aIsChrome) , mDeviceChosen(false) , mSourceSet(aSourceSet) , mManager(MediaManager::GetInstance()) {} ~GetUserMediaTask() { } void Fail(const nsAString& aName, const nsAString& aMessage = EmptyString(), const nsAString& aConstraint = EmptyString()) { RefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage, aConstraint); - auto runnable = MakeRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>>( - mOnSuccess, mOnFailure, *error, mWindowID); + auto errorRunnable = MakeRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>>( + Move(mOnSuccess), Move(mOnFailure), *error, mWindowID); // These should be empty now MOZ_ASSERT(!mOnSuccess); MOZ_ASSERT(!mOnFailure); - NS_DispatchToMainThread(runnable.forget()); + NS_DispatchToMainThread(errorRunnable.forget()); // Do after ErrorCallbackRunnable Run()s, as it checks active window list - NS_DispatchToMainThread(do_AddRef(new GetUserMediaListenerRemove(mWindowID, mListener))); + NS_DispatchToMainThread(NewRunnableMethod<RefPtr<SourceListener>>( + mWindowListener, &GetUserMediaWindowListener::Remove, mSourceListener)); } NS_IMETHOD Run() override { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(mOnSuccess); MOZ_ASSERT(mOnFailure); @@ -1481,18 +1543,19 @@ public: } PeerIdentity* peerIdentity = nullptr; if (!mConstraints.mPeerIdentity.IsEmpty()) { peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity); } NS_DispatchToMainThread(do_AddRef( new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID, - mListener, mPrincipalInfo, - mConstraints, mAudioDevice, mVideoDevice, + mWindowListener, mSourceListener, + mPrincipalInfo, mConstraints, + mAudioDevice, mVideoDevice, peerIdentity))); MOZ_ASSERT(!mOnSuccess); MOZ_ASSERT(!mOnFailure); return NS_OK; } nsresult Denied(const nsAString& aName, @@ -1510,18 +1573,17 @@ public: nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget(); if (auto* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID)) { RefPtr<MediaStreamError> error = new MediaStreamError(window->AsInner(), aName, aMessage); onFailure->OnError(error); } // Should happen *after* error runs for consistency, but may not matter - RefPtr<MediaManager> manager(MediaManager::GetInstance()); - manager->RemoveFromWindowList(mWindowID, mListener); + mWindowListener->Remove(mSourceListener); } else { // This will re-check the window being alive on main-thread // and remove the listener on MainThread as well Fail(aName, aMessage); } MOZ_ASSERT(!mOnSuccess); MOZ_ASSERT(!mOnFailure); @@ -1559,17 +1621,18 @@ public: } private: MediaStreamConstraints mConstraints; nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess; nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure; uint64_t mWindowID; - RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; + RefPtr<GetUserMediaWindowListener> mWindowListener; + RefPtr<SourceListener> mSourceListener; RefPtr<AudioDevice> mAudioDevice; RefPtr<VideoDevice> mVideoDevice; MediaEnginePrefs mPrefs; ipc::PrincipalInfo mPrincipalInfo; bool mIsChrome; bool mDeviceChosen; public: @@ -1890,57 +1953,43 @@ MediaManager::PostTask(already_AddRefed< } NS_ASSERTION(Get(), "MediaManager singleton?"); NS_ASSERTION(Get()->mMediaThread, "No thread yet"); Get()->mMediaThread->message_loop()->PostTask(Move(task)); } /* static */ nsresult MediaManager::NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow, - const nsString& aMsg, - const bool& aIsAudio, - const bool& aIsVideo) + const nsString& aMsg) { NS_ENSURE_ARG(aWindow); nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); if (!obs) { NS_WARNING("Could not get the Observer service for GetUserMedia recording notification."); return NS_ERROR_FAILURE; } RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); - props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio); - props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo); nsCString pageURL; nsCOMPtr<nsIURI> docURI = aWindow->GetDocumentURI(); NS_ENSURE_TRUE(docURI, NS_ERROR_FAILURE); nsresult rv = docURI->GetSpec(pageURL); NS_ENSURE_SUCCESS(rv, rv); NS_ConvertUTF8toUTF16 requestURL(pageURL); props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL); obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props), "recording-device-events", aMsg.get()); - // Forward recording events to parent process. - // The events are gathered in chrome process and used for recording indicator - if (!XRE_IsParentProcess()) { - Unused << - dom::ContentChild::GetSingleton()->SendRecordingDeviceEvents(aMsg, - requestURL, - aIsAudio, - aIsVideo); - } - return NS_OK; } int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback) { bool fakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn; MediaManager::PostTask(NewTaskFrom([fakeDeviceChangeEventOn]() { RefPtr<MediaManager> manager = MediaManager_GetInstance(); @@ -2227,25 +2276,30 @@ if (privileged) { cs.mMediaSource = ac.mMediaSource; } } } } else if (IsOn(c.mAudio)) { audioType = MediaSourceEnum::Microphone; } - StreamListeners* listeners = AddWindowID(windowID); - - // Create a disabled listener to act as a placeholder - RefPtr<GetUserMediaCallbackMediaStreamListener> listener = - new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID, - MakePrincipalHandle(principal)); - - // No need for locking because we always do this in the main thread. - listeners->AppendElement(listener); + // Create a window listener if it doesn't already exist. + RefPtr<GetUserMediaWindowListener> windowListener = + GetWindowListener(windowID); + if (windowListener) { + PrincipalHandle existingPrincipalHandle = windowListener->GetPrincipalHandle(); + MOZ_ASSERT(PrincipalHandleMatches(existingPrincipalHandle, principal)); + } else { + windowListener = new GetUserMediaWindowListener(mMediaThread, windowID, + MakePrincipalHandle(principal)); + AddWindowID(windowID, windowListener); + } + + RefPtr<SourceListener> sourceListener = new SourceListener(); + windowListener->Register(sourceListener); if (!privileged) { // Check if this site has had persistent permissions denied. nsCOMPtr<nsIPermissionManager> permManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION; @@ -2264,17 +2318,17 @@ if (privileged) { } if ((!IsOn(c.mAudio) && !IsOn(c.mVideo)) || (IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) || (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION)) { RefPtr<MediaStreamError> error = new MediaStreamError(aWindow, NS_LITERAL_STRING("NotAllowedError")); onFailure->OnError(error); - RemoveFromWindowList(windowID, listener); + windowListener->Remove(sourceListener); return NS_OK; } } // Get list of all devices, with origin-specific device ids. MediaEnginePrefs prefs = mPrefs; @@ -2287,33 +2341,34 @@ if (privileged) { bool askPermission = (!privileged || Preferences::GetBool("media.navigator.permission.force")) && (!fake || Preferences::GetBool("media.navigator.permission.fake")); RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType, audioType, fake); RefPtr<MediaManager> self = this; - p->Then([self, onSuccess, onFailure, windowID, c, listener, askPermission, - prefs, isHTTPS, callID, principalInfo, + p->Then([self, onSuccess, onFailure, windowID, c, windowListener, + sourceListener, askPermission, prefs, isHTTPS, callID, principalInfo, isChrome](SourceSet*& aDevices) mutable { // grab result auto devices = MakeRefPtr<Refcountable<UniquePtr<SourceSet>>>(aDevices); // Ensure that our windowID is still good. if (!nsGlobalWindow::GetInnerWindowWithId(windowID)) { return; } // Apply any constraints. This modifies the passed-in list. RefPtr<PledgeChar> p2 = self->SelectSettings(c, isChrome, devices); p2->Then([self, onSuccess, onFailure, windowID, c, - listener, askPermission, prefs, isHTTPS, callID, principalInfo, - isChrome, devices](const char*& badConstraint) mutable { + windowListener, sourceListener, askPermission, prefs, isHTTPS, + callID, principalInfo, isChrome, devices + ](const char*& badConstraint) mutable { // Ensure that the captured 'this' pointer and our windowID are still good. auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(windowID); RefPtr<nsPIDOMWindowInner> window = globalWindow ? globalWindow->AsInner() : nullptr; if (!MediaManager::Exists() || !window) { return; } @@ -2341,21 +2396,25 @@ if (privileged) { for (auto& device : **devices) { nsresult rv = devicesCopy->AppendElement(device, /*weak =*/ false); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } } - // Pass callbacks and MediaStreamListener along to GetUserMediaTask. - RefPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(), + // Pass callbacks and listeners along to GetUserMediaTask. + RefPtr<GetUserMediaTask> task (new GetUserMediaTask(c, + onSuccess.forget(), onFailure.forget(), - windowID, listener, - prefs, principalInfo, + windowID, + windowListener, + sourceListener, + prefs, + principalInfo, isChrome, devices->release())); // Store the task w/callbacks. self->mActiveCallbacks.Put(callID, task.forget()); // Add a WindowID cross-reference so OnNavigation can tear things down nsTArray<nsString>* array; if (!self->mCallIds.Get(windowID, &array)) { @@ -2545,43 +2604,50 @@ MediaManager::EnumerateDevices(nsPIDOMWi nsIDOMGetUserMediaErrorCallback* aOnFailure) { MOZ_ASSERT(NS_IsMainThread()); NS_ENSURE_TRUE(!sInShutdown, NS_ERROR_FAILURE); nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess); nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure); uint64_t windowId = aWindow->WindowID(); - StreamListeners* listeners = AddWindowID(windowId); - nsIPrincipal* principal = aWindow->GetExtantDoc()->NodePrincipal(); - // Create a disabled listener to act as a placeholder - RefPtr<GetUserMediaCallbackMediaStreamListener> listener = - new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowId, - MakePrincipalHandle(principal)); - - // No need for locking because we always do this in the main thread. - listeners->AppendElement(listener); + RefPtr<GetUserMediaWindowListener> windowListener = + GetWindowListener(windowId); + if (windowListener) { + PrincipalHandle existingPrincipalHandle = + windowListener->GetPrincipalHandle(); + MOZ_ASSERT(PrincipalHandleMatches(existingPrincipalHandle, principal)); + } else { + windowListener = new GetUserMediaWindowListener(mMediaThread, windowId, + MakePrincipalHandle(principal)); + AddWindowID(windowId, windowListener); + } + + // Create an inactive SourceListener to act as a placeholder, so the + // window listener doesn't clean itself up until we're done. + RefPtr<SourceListener> sourceListener = new SourceListener(); + windowListener->Register(sourceListener); bool fake = Preferences::GetBool("media.navigator.streams.fake"); RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowId, MediaSourceEnum::Camera, MediaSourceEnum::Microphone, fake); - p->Then([onSuccess, windowId, listener](SourceSet*& aDevices) mutable { + p->Then([onSuccess, windowListener, sourceListener](SourceSet*& aDevices) mutable { UniquePtr<SourceSet> devices(aDevices); // grab result - RefPtr<MediaManager> mgr = MediaManager_GetInstance(); - mgr->RemoveFromWindowList(windowId, listener); + DebugOnly<bool> rv = windowListener->Remove(sourceListener); + MOZ_ASSERT(rv); nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices); onSuccess->OnSuccess(array); - }, [onFailure, windowId, listener](MediaStreamError*& reason) mutable { - RefPtr<MediaManager> mgr = MediaManager_GetInstance(); - mgr->RemoveFromWindowList(windowId, listener); + }, [onFailure, windowListener, sourceListener](MediaStreamError*& reason) mutable { + DebugOnly<bool> rv = windowListener->Remove(sourceListener); + MOZ_ASSERT(rv); onFailure->OnError(reason); }); return NS_OK; } /* * GetUserMediaDevices - called by the UI-part of getUserMedia from chrome JS. */ @@ -2637,34 +2703,31 @@ MediaManager::GetBackend(uint64_t aWindo #endif } return mBackend; } static void StopSharingCallback(MediaManager *aThis, uint64_t aWindowID, - StreamListeners *aListeners, + GetUserMediaWindowListener *aListener, void *aData) { MOZ_ASSERT(NS_IsMainThread()); - if (aListeners) { - auto length = aListeners->Length(); - for (size_t i = 0; i < length; ++i) { - GetUserMediaCallbackMediaStreamListener *listener = aListeners->ElementAt(i); - - if (listener->Stream()) { // aka HasBeenActivate()ed - listener->Stop(); - } - listener->Remove(); - listener->StopSharing(); - } - aListeners->Clear(); - aThis->RemoveWindowID(aWindowID); + + // Grab a strong ref since RemoveAll() might destroy the listener mid-way + // when clearing the mActiveWindows reference. + RefPtr<GetUserMediaWindowListener> listener(aListener); + if (!listener) { + return; } + + listener->Stop(); + listener->RemoveAll(); + MOZ_ASSERT(!aThis->GetWindowListener(aWindowID)); } void MediaManager::OnNavigation(uint64_t aWindowID) { MOZ_ASSERT(NS_IsMainThread()); LOG(("OnNavigation for %" PRIu64, aWindowID)); @@ -2683,16 +2746,17 @@ MediaManager::OnNavigation(uint64_t aWin // This is safe since we're on main-thread, and the windowlist can only // be added to from the main-thread auto* window = nsGlobalWindow::GetInnerWindowWithId(aWindowID); if (window) { IterateWindowListeners(window->AsInner(), StopSharingCallback, nullptr); } else { RemoveWindowID(aWindowID); } + MOZ_ASSERT(!GetWindowListener(aWindowID)); RemoveMediaDevicesCallback(aWindowID); } void MediaManager::RemoveMediaDevicesCallback(uint64_t aWindowID) { MutexAutoLock lock(mCallbackMutex); @@ -2706,30 +2770,30 @@ MediaManager::RemoveMediaDevicesCallback if (window && window->WindowID() == aWindowID) { DeviceChangeCallback::RemoveDeviceChangeCallbackLocked(observer); return; } } } } -StreamListeners* -MediaManager::AddWindowID(uint64_t aWindowId) +void +MediaManager::AddWindowID(uint64_t aWindowId, + GetUserMediaWindowListener* aListener) { MOZ_ASSERT(NS_IsMainThread()); // Store the WindowID in a hash table and mark as active. The entry is removed // when this window is closed or navigated away from. // This is safe since we're on main-thread, and the windowlist can only // be invalidated from the main-thread (see OnNavigation) - StreamListeners* listeners = GetActiveWindows()->Get(aWindowId); - if (!listeners) { - listeners = new StreamListeners; - GetActiveWindows()->Put(aWindowId, listeners); + if (IsWindowStillActive(aWindowId)) { + MOZ_ASSERT(false, "Window already added"); + return; } - return listeners; + GetActiveWindows()->Put(aWindowId, aListener); } void MediaManager::RemoveWindowID(uint64_t aWindowId) { mActiveWindows.Remove(aWindowId); // get outer windowID @@ -2754,118 +2818,16 @@ MediaManager::RemoveWindowID(uint64_t aW nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); obs->NotifyObservers(nullptr, "recording-window-ended", data.get()); LOG(("Sent recording-window-ended for window %" PRIu64 " (outer %" PRIu64 ")", aWindowId, outerID)); } void -MediaManager::RemoveFromWindowList(uint64_t aWindowID, - GetUserMediaCallbackMediaStreamListener *aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsString videoRawId; - nsString audioRawId; - nsString videoSourceType; - nsString audioSourceType; - bool hasVideoDevice = aListener->mVideoDevice; - bool hasAudioDevice = aListener->mAudioDevice; - - if (hasVideoDevice) { - aListener->mVideoDevice->GetRawId(videoRawId); - aListener->mVideoDevice->GetMediaSource(videoSourceType); - } - if (hasAudioDevice) { - aListener->mAudioDevice->GetRawId(audioRawId); - aListener->mAudioDevice->GetMediaSource(audioSourceType); - } - - // This is defined as safe on an inactive GUMCMSListener - aListener->Remove(); // really queues the remove - - StreamListeners* listeners = GetWindowListeners(aWindowID); - if (!listeners) { - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(aWindowID); - RefPtr<nsPIDOMWindowInner> window = globalWindow ? globalWindow->AsInner() - : nullptr; - if (window != nullptr) { - RefPtr<GetUserMediaRequest> req = - new GetUserMediaRequest(window, NullString(), NullString()); - obs->NotifyObservers(req, "recording-device-stopped", nullptr); - } - return; - } - listeners->RemoveElement(aListener); - - uint32_t length = listeners->Length(); - - if (hasVideoDevice) { - bool revokeVideoPermission = true; - - for (uint32_t i = 0; i < length; ++i) { - RefPtr<GetUserMediaCallbackMediaStreamListener> listener = - listeners->ElementAt(i); - if (hasVideoDevice && listener->mVideoDevice) { - nsString rawId; - listener->mVideoDevice->GetRawId(rawId); - if (videoRawId.Equals(rawId)) { - revokeVideoPermission = false; - break; - } - } - } - - if (revokeVideoPermission) { - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(aWindowID); - RefPtr<nsPIDOMWindowInner> window = globalWindow ? globalWindow->AsInner() - : nullptr; - RefPtr<GetUserMediaRequest> req = - new GetUserMediaRequest(window, videoRawId, videoSourceType); - obs->NotifyObservers(req, "recording-device-stopped", nullptr); - } - } - - if (hasAudioDevice) { - bool revokeAudioPermission = true; - - for (uint32_t i = 0; i < length; ++i) { - RefPtr<GetUserMediaCallbackMediaStreamListener> listener = - listeners->ElementAt(i); - if (hasAudioDevice && listener->mAudioDevice) { - nsString rawId; - listener->mAudioDevice->GetRawId(rawId); - if (audioRawId.Equals(rawId)) { - revokeAudioPermission = false; - break; - } - } - } - - if (revokeAudioPermission) { - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(aWindowID); - RefPtr<nsPIDOMWindowInner> window = globalWindow ? globalWindow->AsInner() - : nullptr; - RefPtr<GetUserMediaRequest> req = - new GetUserMediaRequest(window, audioRawId, audioSourceType); - obs->NotifyObservers(req, "recording-device-stopped", nullptr); - } - } - - if (length == 0) { - RemoveWindowID(aWindowID); - // listeners has been deleted here - } -} - -void MediaManager::GetPref(nsIPrefBranch *aBranch, const char *aPref, const char *aData, int32_t *aVal) { int32_t temp; if (aData == nullptr || strcmp(aPref,aData) == 0) { if (NS_SUCCEEDED(aBranch->GetIntPref(aPref, &temp))) { *aVal = temp; } @@ -3156,43 +3118,32 @@ nsresult MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray) { MOZ_ASSERT(aArray); nsCOMPtr<nsIMutableArray> array = nsArray::Create(); for (auto iter = mActiveWindows.Iter(); !iter.Done(); iter.Next()) { const uint64_t& id = iter.Key(); - StreamListeners* listeners = iter.UserData(); + RefPtr<GetUserMediaWindowListener> winListener = iter.UserData(); + if (!winListener) { + continue; + } nsPIDOMWindowInner* window = nsGlobalWindow::GetInnerWindowWithId(id)->AsInner(); MOZ_ASSERT(window); // XXXkhuey ... if (!window) { continue; } - // mActiveWindows contains both windows that have requested device - // access and windows that are currently capturing media. We want - // to return only the latter. See bug 975177. - bool capturing = false; - if (listeners) { - uint32_t length = listeners->Length(); - for (uint32_t i = 0; i < length; ++i) { - RefPtr<GetUserMediaCallbackMediaStreamListener> listener = - listeners->ElementAt(i); - if (listener->CapturingVideo() || listener->CapturingAudio() || - listener->CapturingScreen() || listener->CapturingWindow() || - listener->CapturingApplication()) { - capturing = true; - break; - } - } - } - if (capturing) { + + if (winListener->CapturingVideo() || winListener->CapturingAudio() || + winListener->CapturingScreen() || winListener->CapturingWindow() || + winListener->CapturingApplication()) { array->AppendElement(window, /*weak =*/ false); } } array.forget(aArray); return NS_OK; } @@ -3204,49 +3155,45 @@ struct CaptureWindowStateData { bool *mWindowShare; bool *mAppShare; bool *mBrowserShare; }; static void CaptureWindowStateCallback(MediaManager *aThis, uint64_t aWindowID, - StreamListeners *aListeners, + GetUserMediaWindowListener *aListener, void *aData) { struct CaptureWindowStateData *data = (struct CaptureWindowStateData *) aData; - if (aListeners) { - auto length = aListeners->Length(); - for (size_t i = 0; i < length; ++i) { - GetUserMediaCallbackMediaStreamListener *listener = aListeners->ElementAt(i); - - if (listener->CapturingVideo()) { - *data->mVideo = true; - } - if (listener->CapturingAudio()) { - *data->mAudio = true; - } - if (listener->CapturingScreen()) { - *data->mScreenShare = true; - } - if (listener->CapturingWindow()) { - *data->mWindowShare = true; - } - if (listener->CapturingApplication()) { - *data->mAppShare = true; - } - if (listener->CapturingBrowser()) { - *data->mBrowserShare = true; - } - } + if (!aListener) { + return; + } + + if (aListener->CapturingVideo()) { + *data->mVideo = true; + } + if (aListener->CapturingAudio()) { + *data->mAudio = true; + } + if (aListener->CapturingScreen()) { + *data->mScreenShare = true; + } + if (aListener->CapturingWindow()) { + *data->mWindowShare = true; + } + if (aListener->CapturingApplication()) { + *data->mAppShare = true; + } + if (aListener->CapturingBrowser()) { + *data->mBrowserShare = true; } } - NS_IMETHODIMP MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow, bool* aVideo, bool* aAudio, bool *aScreenShare, bool* aWindowShare, bool *aAppShare, bool *aBrowserShare) { MOZ_ASSERT(NS_IsMainThread()); struct CaptureWindowStateData data; @@ -3285,25 +3232,24 @@ MediaManager::SanitizeDeviceIds(int64_t media::SanitizeOriginKeys(aSinceWhen, false); // we fire and forget return NS_OK; } static void StopScreensharingCallback(MediaManager *aThis, uint64_t aWindowID, - StreamListeners *aListeners, + GetUserMediaWindowListener *aListener, void *aData) { - if (aListeners) { - auto length = aListeners->Length(); - for (size_t i = 0; i < length; ++i) { - aListeners->ElementAt(i)->StopSharing(); - } + if (!aListener) { + return; } + + aListener->StopSharing(); } void MediaManager::StopScreensharing(uint64_t aWindowID) { // We need to stop window/screensharing for all streams in all innerwindows that // correspond to that outerwindow. @@ -3318,20 +3264,22 @@ MediaManager::StopScreensharing(uint64_t void MediaManager::IterateWindowListeners(nsPIDOMWindowInner* aWindow, WindowListenerCallback aCallback, void *aData) { // Iterate the docshell tree to find all the child windows, and for each // invoke the callback if (aWindow) { - uint64_t windowID = aWindow->WindowID(); - StreamListeners* listeners = GetActiveWindows()->Get(windowID); - // pass listeners so it can modify/delete the list - (*aCallback)(this, windowID, listeners, aData); + { + uint64_t windowID = aWindow->WindowID(); + GetUserMediaWindowListener* listener = GetWindowListener(windowID); + (*aCallback)(this, windowID, listener, aData); + // NB: `listener` might have been destroyed. + } // iterate any children of *this* window (iframes, etc) nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell(); if (docShell) { int32_t i, count; docShell->GetChildCount(&count); for (i = 0; i < count; ++i) { nsCOMPtr<nsIDocShellTreeItem> item; @@ -3409,72 +3357,416 @@ MediaManager::IsActivelyCapturingOrHasAP if (NS_WARN_IF(NS_FAILED(rv))) { return false; } } return audio == nsIPermissionManager::ALLOW_ACTION || video == nsIPermissionManager::ALLOW_ACTION; } +SourceListener::SourceListener() + : mActivated(false) + , mStopped(false) + , mFinished(false) + , mRemoved(false) + , mAudioStopped(false) + , mVideoStopped(false) + , mMainThreadCheck(nullptr) + , mPrincipalHandle(PRINCIPAL_HANDLE_NONE) + , mWindowListener(nullptr) +{} + void -GetUserMediaCallbackMediaStreamListener::Stop() +SourceListener::Register(GetUserMediaWindowListener* aListener) +{ + LOG(("SourceListener %p registering with window listener %p", this, aListener)); + + if (mWindowListener) { + MOZ_ASSERT(false, "Already registered"); + return; + } + if (mActivated) { + MOZ_ASSERT(false, "Already activated"); + return; + } + if (!aListener) { + MOZ_ASSERT(false, "No listener"); + return; + } + mPrincipalHandle = aListener->GetPrincipalHandle(); + mWindowListener = aListener; +} + +void +SourceListener::Activate(SourceMediaStream* aStream, + AudioDevice* aAudioDevice, + VideoDevice* aVideoDevice) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread"); + + LOG(("SourceListener %p activating audio=%p video=%p", this, aAudioDevice, aVideoDevice)); + + if (mActivated) { + MOZ_ASSERT(false, "Already activated"); + return; + } + + mActivated = true; + mMainThreadCheck = PR_GetCurrentThread(); + mStream = aStream; + mAudioDevice = aAudioDevice; + mVideoDevice = aVideoDevice; + mStream->AddListener(this); +} + +void +SourceListener::Stop() +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread"); + if (mStopped) { return; } - // We can't take a chance on blocking here, so proxy this to another - // thread. - // Pass a ref to us (which is threadsafe) so it can query us for the - // source stream info. - RefPtr<MediaOperationTask> mediaOperation = - new MediaOperationTask(MEDIA_STOP, - this, nullptr, nullptr, - !mAudioStopped ? mAudioDevice.get() : nullptr, - !mVideoStopped ? mVideoDevice.get() : nullptr, - false, mWindowID, nullptr); - MediaManager::PostTask(mediaOperation.forget()); - mStopped = mAudioStopped = mVideoStopped = true; + LOG(("SourceListener %p stopping", this)); + + // StopSharing() has some special logic, at least for audio capture. + // It must be called when all tracks have stopped, before setting mStopped. + StopSharing(); + + mStopped = true; + + if (mAudioDevice && !mAudioStopped) { + StopTrack(kAudioTrack); + } + if (mVideoDevice && !mVideoStopped) { + StopTrack(kVideoTrack); + } + RefPtr<SourceMediaStream> source = GetSourceStream(); + MediaManager::PostTask(NewTaskFrom([source]() { + MOZ_ASSERT(MediaManager::IsInMediaThread()); + source->EndAllTrackAndFinish(); + })); } -// Doesn't kill audio void -GetUserMediaCallbackMediaStreamListener::StopSharing() +SourceListener::Remove() { MOZ_ASSERT(NS_IsMainThread()); + if (!mStream || mRemoved) { + return; + } + + LOG(("SourceListener %p removed on purpose, mFinished = %d", this, (int) mFinished)); + mRemoved = true; // RemoveListener is async, avoid races + mWindowListener = nullptr; + + // If it's destroyed, don't call - listener will be removed and we'll be notified! + if (!mStream->IsDestroyed()) { + mStream->RemoveListener(this); + } +} + +void +SourceListener::StopTrack(TrackID aTrackID) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread"); + + RefPtr<MediaDevice> device; + RefPtr<SourceMediaStream> source; + + switch (aTrackID) { + case kAudioTrack: { + LOG(("SourceListener %p stopping audio track %d", this, aTrackID)); + if (!mAudioDevice) { + NS_ASSERTION(false, "Can't stop audio. No device."); + return; + } + if (mAudioStopped) { + // Audio already stopped + return; + } + device = mAudioDevice; + source = GetSourceStream(); + mAudioStopped = true; + break; + } + case kVideoTrack: { + LOG(("SourceListener %p stopping video track %d", this, aTrackID)); + if (!mVideoDevice) { + NS_ASSERTION(false, "Can't stop video. No device."); + return; + } + if (mVideoStopped) { + // Video already stopped + return; + } + device = mVideoDevice; + source = GetSourceStream(); + mVideoStopped = true; + break; + } + default: { + MOZ_ASSERT(false, "Unknown track id"); + return; + } + } + + MediaManager::PostTask(NewTaskFrom([device, source, aTrackID]() { + device->GetSource()->Stop(source, aTrackID); + device->Deallocate(); + })); + + if ((!mAudioDevice || mAudioStopped) && + (!mVideoDevice || mVideoStopped)) { + LOG(("SourceListener %p this was the last track stopped", this)); + Stop(); + } + + if (!mWindowListener) { + MOZ_ASSERT(false, "Should still have window listener"); + return; + } + mWindowListener->NotifySourceTrackStopped(); +} + +void +SourceListener::StopSharing() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(mWindowListener); + + if (mStopped) { + return; + } + + LOG(("SourceListener %p StopSharing", this)); + if (mVideoDevice && (mVideoDevice->GetMediaSource() == MediaSourceEnum::Screen || mVideoDevice->GetMediaSource() == MediaSourceEnum::Application || mVideoDevice->GetMediaSource() == MediaSourceEnum::Window)) { // We want to stop the whole stream if there's no audio; // just the video track if we have both. // StopTrack figures this out for us. StopTrack(kVideoTrack); - } else if (mAudioDevice && - mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) { - nsCOMPtr<nsPIDOMWindowInner> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID)->AsInner(); - MOZ_ASSERT(window); + } + if (mAudioDevice && + mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) { + uint64_t windowID = mWindowListener->WindowID(); + nsCOMPtr<nsPIDOMWindowInner> window = nsGlobalWindow::GetInnerWindowWithId(windowID)->AsInner(); + MOZ_RELEASE_ASSERT(window); window->SetAudioCapture(false); MediaStreamGraph* graph = MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, dom::AudioChannel::Normal); - graph->UnregisterCaptureStreamForWindow(mWindowID); + graph->UnregisterCaptureStreamForWindow(windowID); mStream->Destroy(); } } -// ApplyConstraints for track - -auto -GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( - nsPIDOMWindowInner* aWindow, - TrackID aTrackID, - const MediaTrackConstraints& aConstraints, - dom::CallerType aCallerType) -> already_AddRefed<PledgeVoid> +SourceMediaStream* +SourceListener::GetSourceStream() +{ + NS_ASSERTION(mStream,"Getting stream from never-activated SourceListener"); + if (!mStream) { + return nullptr; + } + return mStream->AsSourceStream(); +} + +void +SourceListener::GetSettings(dom::MediaTrackSettings& aOutSettings, TrackID aTrackID) +{ + switch (aTrackID) { + case kVideoTrack: { + if (mVideoDevice) { + mVideoDevice->GetSource()->GetSettings(aOutSettings); + } + break; + } + case kAudioTrack: { + if (mAudioDevice) { + mAudioDevice->GetSource()->GetSettings(aOutSettings); + } + break; + } + default: { + MOZ_ASSERT(false, "Unknown track id"); + } + } +} + +// Proxy NotifyPull() to sources +void +SourceListener::NotifyPull(MediaStreamGraph* aGraph, + StreamTime aDesiredTime) +{ + // Currently audio sources ignore NotifyPull, but they could + // watch it especially for fake audio. + if (mAudioDevice) { + mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack, + aDesiredTime, mPrincipalHandle); + } + if (mVideoDevice) { + mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack, + aDesiredTime, mPrincipalHandle); + } +} + +void +SourceListener::NotifyEvent(MediaStreamGraph* aGraph, + MediaStreamGraphEvent aEvent) +{ + nsresult rv; + nsCOMPtr<nsIThread> thread; + + switch (aEvent) { + case MediaStreamGraphEvent::EVENT_FINISHED: + rv = NS_GetMainThread(getter_AddRefs(thread)); + if (NS_WARN_IF(NS_FAILED(rv))) { + NS_ASSERTION(false, "Mainthread not available; running on current thread"); + // Ensure this really *was* MainThread (NS_GetCurrentThread won't work) + MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread()); + NotifyFinished(); + return; + } + thread->Dispatch(NewRunnableMethod(this, &SourceListener::NotifyFinished), + NS_DISPATCH_NORMAL); + break; + case MediaStreamGraphEvent::EVENT_REMOVED: + rv = NS_GetMainThread(getter_AddRefs(thread)); + if (NS_WARN_IF(NS_FAILED(rv))) { + NS_ASSERTION(false, "Mainthread not available; running on current thread"); + // Ensure this really *was* MainThread (NS_GetCurrentThread won't work) + MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread()); + NotifyRemoved(); + return; + } + thread->Dispatch(NewRunnableMethod(this, &SourceListener::NotifyRemoved), + NS_DISPATCH_NORMAL); + break; + case MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS: + NotifyDirectListeners(aGraph, true); + break; + case MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS: + NotifyDirectListeners(aGraph, false); + break; + default: + break; + } +} + +void +SourceListener::NotifyFinished() +{ + MOZ_ASSERT(NS_IsMainThread()); + mFinished = true; + if (!mWindowListener) { + // Removed explicitly before finished. + return; + } + + LOG(("SourceListener %p NotifyFinished", this)); + + Stop(); // we know it's been activated + mWindowListener->Remove(this); +} + +void +SourceListener::NotifyRemoved() +{ + MOZ_ASSERT(NS_IsMainThread()); + LOG(("SourceListener removed, mFinished = %d", (int) mFinished)); + mRemoved = true; + + if (!mFinished) { + NotifyFinished(); + } + + mWindowListener = nullptr; +} + +void +SourceListener::NotifyDirectListeners(MediaStreamGraph* aGraph, + bool aHasListeners) +{ + if (!mVideoDevice) { + return; + } + + auto& videoDevice = mVideoDevice; + MediaManager::PostTask(NewTaskFrom([videoDevice, aHasListeners]() { + videoDevice->GetSource()->SetDirectListeners(aHasListeners); + return NS_OK; + })); +} + +bool +SourceListener::CapturingVideo() const +{ + MOZ_ASSERT(NS_IsMainThread()); + return mActivated && mVideoDevice && !mVideoStopped && + !mVideoDevice->GetSource()->IsAvailable() && + mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera && + (!mVideoDevice->GetSource()->IsFake() || + Preferences::GetBool("media.navigator.permission.fake")); +} + +bool +SourceListener::CapturingAudio() const +{ + MOZ_ASSERT(NS_IsMainThread()); + return mActivated && mAudioDevice && !mAudioStopped && + !mAudioDevice->GetSource()->IsAvailable() && + (!mAudioDevice->GetSource()->IsFake() || + Preferences::GetBool("media.navigator.permission.fake")); +} + +bool +SourceListener::CapturingScreen() const +{ + MOZ_ASSERT(NS_IsMainThread()); + return mActivated && mVideoDevice && !mVideoStopped && + !mVideoDevice->GetSource()->IsAvailable() && + mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen; +} + +bool +SourceListener::CapturingWindow() const +{ + MOZ_ASSERT(NS_IsMainThread()); + return mActivated && mVideoDevice && !mVideoStopped && + !mVideoDevice->GetSource()->IsAvailable() && + mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window; +} + +bool +SourceListener::CapturingApplication() const +{ + MOZ_ASSERT(NS_IsMainThread()); + return mActivated && mVideoDevice && !mVideoStopped && + !mVideoDevice->GetSource()->IsAvailable() && + mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application; +} + +bool +SourceListener::CapturingBrowser() const +{ + MOZ_ASSERT(NS_IsMainThread()); + return mActivated && mVideoDevice && !mVideoStopped && + !mVideoDevice->GetSource()->IsAvailable() && + mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser; +} + +already_AddRefed<PledgeVoid> +SourceListener::ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow, + TrackID aTrackID, + const dom::MediaTrackConstraints& aConstraints, + dom::CallerType aCallerType) { MOZ_ASSERT(NS_IsMainThread()); RefPtr<PledgeVoid> p = new PledgeVoid(); // XXX to support multiple tracks of a type in a stream, this should key off // the TrackID and not just the type RefPtr<AudioDevice> audioDevice = aTrackID == kAudioTrack ? mAudioDevice.get() : nullptr; @@ -3552,167 +3844,76 @@ GetUserMediaCallbackMediaStreamListener: } } return NS_OK; })); })); return p.forget(); } -// Stop backend for track +PrincipalHandle +SourceListener::GetPrincipalHandle() const +{ + return mPrincipalHandle; +} + +// Doesn't kill audio +void +GetUserMediaWindowListener::StopSharing() +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread"); + + for (auto& source : mActiveListeners) { + source->StopSharing(); + } +} void -GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID) +GetUserMediaWindowListener::NotifySourceTrackStopped() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aTrackID == kAudioTrack || aTrackID == kVideoTrack); - - // XXX to support multiple tracks of a type in a stream, this should key off - // the TrackID and not just hard coded values. - - bool stopAudio = aTrackID == kAudioTrack; - bool stopVideo = aTrackID == kVideoTrack; - - if (mStopped || - (stopAudio && (mAudioStopped || !mAudioDevice)) || - (stopVideo && (mVideoStopped || !mVideoDevice))) - { - LOG(("Can't stop gUM track %d (%s), exists=%d, stopped=%d", - aTrackID, - stopAudio ? "audio" : "video", - stopAudio ? !!mAudioDevice : !!mVideoDevice, - stopAudio ? mAudioStopped : mVideoStopped)); - return; - } - - if ((stopAudio || mAudioStopped || !mAudioDevice) && - (stopVideo || mVideoStopped || !mVideoDevice)) { - Stop(); - return; - } // We wait until stable state before notifying chrome so chrome only does one // update if more tracks are stopped in this event loop. - mAudioStopPending |= stopAudio; - mVideoStopPending |= stopVideo; - if (mChromeNotificationTaskPosted) { return; } nsCOMPtr<nsIRunnable> runnable = - NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyChromeOfTrackStops); + NewRunnableMethod(this, &GetUserMediaWindowListener::NotifyChromeOfTrackStops); nsContentUtils::RunInStableState(runnable.forget()); mChromeNotificationTaskPosted = true; } void -GetUserMediaCallbackMediaStreamListener::NotifyChromeOfTrackStops() +GetUserMediaWindowListener::NotifyChromeOfTrackStops() { MOZ_ASSERT(mChromeNotificationTaskPosted); mChromeNotificationTaskPosted = false; - // We make sure these are always reset. - bool stopAudio = mAudioStopPending; - bool stopVideo = mVideoStopPending; - mAudioStopPending = false; - mVideoStopPending = false; - - if (mStopped) { - // The entire capture was stopped while we were waiting for stable state. - return; - } - - MOZ_ASSERT(stopAudio || stopVideo); - MOZ_ASSERT(!stopAudio || !mAudioStopped, - "If there's a pending stop for audio, audio must not have been stopped"); - MOZ_ASSERT(!stopAudio || mAudioDevice, - "If there's a pending stop for audio, there must be an audio device"); - MOZ_ASSERT(!stopVideo || !mVideoStopped, - "If there's a pending stop for video, video must not have been stopped"); - MOZ_ASSERT(!stopVideo || mVideoDevice, - "If there's a pending stop for video, there must be a video device"); - - if ((stopAudio || mAudioStopped || !mAudioDevice) && - (stopVideo || mVideoStopped || !mVideoDevice)) { - // All tracks stopped. - Stop(); - return; - } - - mAudioStopped |= stopAudio; - mVideoStopped |= stopVideo; - - RefPtr<MediaOperationTask> mediaOperation = - new MediaOperationTask(MEDIA_STOP_TRACK, - this, nullptr, nullptr, - stopAudio ? mAudioDevice.get() : nullptr, - stopVideo ? mVideoDevice.get() : nullptr, - false , mWindowID, nullptr); - MediaManager::PostTask(mediaOperation.forget()); -} - -void -GetUserMediaCallbackMediaStreamListener::NotifyFinished() -{ - MOZ_ASSERT(NS_IsMainThread()); - mFinished = true; - Stop(); // we know it's been activated - - RefPtr<MediaManager> manager(MediaManager::GetIfExists()); - if (manager) { - manager->RemoveFromWindowList(mWindowID, this); - } else { - NS_WARNING("Late NotifyFinished after MediaManager shutdown"); - } -} - -// Called from the MediaStreamGraph thread -void -GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph, - bool aHasListeners) -{ - RefPtr<MediaOperationTask> mediaOperation = - new MediaOperationTask(MEDIA_DIRECT_LISTENERS, - this, nullptr, nullptr, - mAudioDevice, mVideoDevice, - aHasListeners, mWindowID, nullptr); - MediaManager::PostTask(mediaOperation.forget()); -} - -// this can be in response to our own RemoveListener() (via ::Remove()), or -// because the DOM GC'd the DOMLocalMediaStream/etc we're attached to. -void -GetUserMediaCallbackMediaStreamListener::NotifyRemoved() -{ - MOZ_ASSERT(NS_IsMainThread()); - MM_LOG(("Listener removed by DOM Destroy(), mFinished = %d", (int) mFinished)); - mRemoved = true; - - if (!mFinished) { - NotifyFinished(); - } + NS_DispatchToMainThread(do_AddRef(new GetUserMediaNotificationEvent( + GetUserMediaNotificationEvent::STOPPING, mWindowID))); } GetUserMediaNotificationEvent::GetUserMediaNotificationEvent( - GetUserMediaCallbackMediaStreamListener* aListener, GetUserMediaStatus aStatus, - bool aIsAudio, bool aIsVideo, uint64_t aWindowID) -: mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio) -, mIsVideo(aIsVideo), mWindowID(aWindowID) {} + uint64_t aWindowID) +: mStatus(aStatus), mWindowID(aWindowID) {} GetUserMediaNotificationEvent::GetUserMediaNotificationEvent( GetUserMediaStatus aStatus, already_AddRefed<DOMMediaStream> aStream, - OnTracksAvailableCallback* aOnTracksAvailableCallback, - bool aIsAudio, bool aIsVideo, uint64_t aWindowID, + already_AddRefed<Refcountable<UniquePtr<OnTracksAvailableCallback>>> aOnTracksAvailableCallback, + uint64_t aWindowID, already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError) -: mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback), - mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID), +: mStream(aStream), + mOnTracksAvailableCallback(aOnTracksAvailableCallback), + mStatus(aStatus), + mWindowID(aWindowID), mOnFailure(aError) {} GetUserMediaNotificationEvent::~GetUserMediaNotificationEvent() { } NS_IMETHODIMP GetUserMediaNotificationEvent::Run() { @@ -3722,23 +3923,22 @@ GetUserMediaNotificationEvent::Run() // Otherwise this object might be destroyed off the main thread, // releasing DOMMediaStream off the main thread, which is not allowed. RefPtr<DOMMediaStream> stream = mStream.forget(); nsString msg; switch (mStatus) { case STARTING: msg = NS_LITERAL_STRING("starting"); - stream->OnTracksAvailable(mOnTracksAvailableCallback.forget()); + stream->OnTracksAvailable(mOnTracksAvailableCallback->release()); break; case STOPPING: - case STOPPED_TRACK: msg = NS_LITERAL_STRING("shutdown"); break; } RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); - return MediaManager::NotifyRecordingStatusChange(window->AsInner(), msg, mIsAudio, mIsVideo); + return MediaManager::NotifyRecordingStatusChange(window->AsInner(), msg); } } // namespace mozilla
--- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -50,22 +50,20 @@ struct MediaTrackConstraints; struct MediaTrackConstraintSet; enum class CallerType : uint32_t; } // namespace dom namespace ipc { class PrincipalInfo; } +class GetUserMediaTask; +class GetUserMediaWindowListener; class MediaManager; -class GetUserMediaCallbackMediaStreamListener; -class GetUserMediaTask; - -extern LogModule* GetMediaManagerLog(); -#define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg) +class SourceListener; class MediaDevice : public nsIMediaDevice { public: typedef MediaEngineSource Source; NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIMEDIADEVICE @@ -131,76 +129,71 @@ public: }; class GetUserMediaNotificationEvent: public Runnable { public: enum GetUserMediaStatus { STARTING, STOPPING, - STOPPED_TRACK, }; - GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener, - GetUserMediaStatus aStatus, - bool aIsAudio, bool aIsVideo, uint64_t aWindowID); + GetUserMediaNotificationEvent(GetUserMediaStatus aStatus, + uint64_t aWindowID); GetUserMediaNotificationEvent(GetUserMediaStatus aStatus, already_AddRefed<DOMMediaStream> aStream, - OnTracksAvailableCallback* aOnTracksAvailableCallback, - bool aIsAudio, bool aIsVideo, uint64_t aWindowID, + already_AddRefed<media::Refcountable<UniquePtr<OnTracksAvailableCallback>>> aOnTracksAvailableCallback, + uint64_t aWindowID, already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError); virtual ~GetUserMediaNotificationEvent(); NS_IMETHOD Run() override; protected: - RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe + RefPtr<GetUserMediaWindowListener> mListener; // threadsafe RefPtr<DOMMediaStream> mStream; - nsAutoPtr<OnTracksAvailableCallback> mOnTracksAvailableCallback; + RefPtr<media::Refcountable<UniquePtr<OnTracksAvailableCallback>>> mOnTracksAvailableCallback; GetUserMediaStatus mStatus; - bool mIsAudio; - bool mIsVideo; uint64_t mWindowID; RefPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure; }; typedef enum { - MEDIA_START, MEDIA_STOP, MEDIA_STOP_TRACK, MEDIA_DIRECT_LISTENERS, } MediaOperation; class ReleaseMediaOperationResource : public Runnable { public: - ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream, - OnTracksAvailableCallback* aOnTracksAvailableCallback): + ReleaseMediaOperationResource( + already_AddRefed<DOMMediaStream> aStream, + already_AddRefed<media::Refcountable<UniquePtr<OnTracksAvailableCallback>>> aOnTracksAvailableCallback): mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback) {} NS_IMETHOD Run() override {return NS_OK;} private: RefPtr<DOMMediaStream> mStream; - nsAutoPtr<OnTracksAvailableCallback> mOnTracksAvailableCallback; + RefPtr<media::Refcountable<UniquePtr<OnTracksAvailableCallback>>> mOnTracksAvailableCallback; }; -typedef nsTArray<RefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners; -typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable; +typedef nsRefPtrHashtable<nsUint64HashKey, GetUserMediaWindowListener> WindowTable; // we could add MediaManager if needed typedef void (*WindowListenerCallback)(MediaManager *aThis, uint64_t aWindowID, - StreamListeners *aListeners, + GetUserMediaWindowListener *aListener, void *aData); class MediaManager final : public nsIMediaManagerService, public nsIObserver ,public DeviceChangeCallback { - friend GetUserMediaCallbackMediaStreamListener; + friend SourceListener; public: static already_AddRefed<MediaManager> GetInstance(); // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread // from MediaManager thread. static MediaManager* Get(); static MediaManager* GetIfExists(); @@ -211,37 +204,41 @@ public: #endif static bool Exists() { return !!sSingleton; } static nsresult NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow, - const nsString& aMsg, - const bool& aIsAudio, - const bool& aIsVideo); + const nsString& aMsg); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSIMEDIAMANAGERSERVICE media::Parent<media::NonE10s>* GetNonE10sParent(); MediaEngine* GetBackend(uint64_t aWindowId = 0); - StreamListeners *GetWindowListeners(uint64_t aWindowId) { + + WindowTable *GetActiveWindows() { MOZ_ASSERT(NS_IsMainThread()); - return mActiveWindows.Get(aWindowId); + return &mActiveWindows; } + GetUserMediaWindowListener *GetWindowListener(uint64_t aWindowId) { + MOZ_ASSERT(NS_IsMainThread()); + return mActiveWindows.GetWeak(aWindowId); + } + void AddWindowID(uint64_t aWindowId, GetUserMediaWindowListener *aListener); void RemoveWindowID(uint64_t aWindowId); bool IsWindowStillActive(uint64_t aWindowId) { - return !!GetWindowListeners(aWindowId); + return !!GetWindowListener(aWindowId); } // Note: also calls aListener->Remove(), even if inactive void RemoveFromWindowList(uint64_t aWindowID, - GetUserMediaCallbackMediaStreamListener *aListener); + GetUserMediaWindowListener *aListener); nsresult GetUserMedia( nsPIDOMWindowInner* aWindow, const dom::MediaStreamConstraints& aConstraints, nsIDOMGetUserMediaSuccessCallback* onSuccess, nsIDOMGetUserMediaErrorCallback* onError, dom::CallerType aCallerType); @@ -288,22 +285,16 @@ private: dom::MediaSourceEnum aAudioSrcType, bool aFake = false); already_AddRefed<PledgeChar> SelectSettings( dom::MediaStreamConstraints& aConstraints, bool aIsChrome, RefPtr<media::Refcountable<UniquePtr<SourceSet>>>& aSources); - StreamListeners* AddWindowID(uint64_t aWindowId); - WindowTable *GetActiveWindows() { - MOZ_ASSERT(NS_IsMainThread()); - return &mActiveWindows; - } - void GetPref(nsIPrefBranch *aBranch, const char *aPref, const char *aData, int32_t *aVal); void GetPrefBool(nsIPrefBranch *aBranch, const char *aPref, const char *aData, bool *aVal); void GetPrefs(nsIPrefBranch *aBranch, const char *aData); // Make private because we want only one instance of this class MediaManager();
new file mode 100644 --- /dev/null +++ b/dom/media/platforms/agnostic/AOMDecoder.cpp @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "AOMDecoder.h" +#include "MediaResult.h" +#include "TimeUnits.h" +#include "aom/aomdx.h" +#include "gfx2DGlue.h" +#include "mozilla/PodOperations.h" +#include "mozilla/SyncRunnable.h" +#include "nsError.h" +#include "prsystem.h" + +#include <algorithm> + +#undef LOG +#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("AOMDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) +#define LOG_RESULT(code, message, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("AOMDecoder::%s: %s (code %d) " message, __func__, aom_codec_err_to_string(code), (int)code, ##__VA_ARGS__)) + +namespace mozilla { + +using namespace gfx; +using namespace layers; + + +static MediaResult +InitContext(aom_codec_ctx_t* aCtx, + const VideoInfo& aInfo) +{ + aom_codec_iface_t* dx = aom_codec_av1_dx(); + if (!dx) { + return MediaResult(NS_ERROR_FAILURE, + RESULT_DETAIL("Couldn't get AV1 decoder interface.")); + } + + int decode_threads = 2; + if (aInfo.mDisplay.width >= 2048) { + decode_threads = 8; + } + else if (aInfo.mDisplay.width >= 1024) { + decode_threads = 4; + } + decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors()); + + aom_codec_dec_cfg_t config; + PodZero(&config); + config.threads = decode_threads; + config.w = config.h = 0; // set after decode + + aom_codec_flags_t flags = 0; + + auto res = aom_codec_dec_init(aCtx, dx, &config, flags); + if (res != AOM_CODEC_OK) { + LOG_RESULT(res, "Codec initialization failed!"); + return MediaResult(NS_ERROR_FAILURE, + RESULT_DETAIL("AOM error initializing AV1 decoder: %s", + aom_codec_err_to_string(res))); + } + return NS_OK; +} + +AOMDecoder::AOMDecoder(const CreateDecoderParams& aParams) + : mImageContainer(aParams.mImageContainer) + , mTaskQueue(aParams.mTaskQueue) + , mInfo(aParams.VideoConfig()) +{ + PodZero(&mCodec); +} + +AOMDecoder::~AOMDecoder() +{ +} + +RefPtr<ShutdownPromise> +AOMDecoder::Shutdown() +{ + RefPtr<AOMDecoder> self = this; + return InvokeAsync(mTaskQueue, __func__, [self, this]() { + auto res = aom_codec_destroy(&mCodec); + if (res != AOM_CODEC_OK) { + LOG_RESULT(res, "aom_codec_destroy"); + } + return ShutdownPromise::CreateAndResolve(true, __func__); + }); +} + +RefPtr<MediaDataDecoder::InitPromise> +AOMDecoder::Init() +{ + if (NS_FAILED(InitContext(&mCodec, mInfo))) { + return AOMDecoder::InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, + __func__); + } + return AOMDecoder::InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, + __func__); +} + +RefPtr<MediaDataDecoder::FlushPromise> +AOMDecoder::Flush() +{ + return InvokeAsync(mTaskQueue, __func__, []() { + return FlushPromise::CreateAndResolve(true, __func__); + }); +} + +RefPtr<MediaDataDecoder::DecodePromise> +AOMDecoder::ProcessDecode(MediaRawData* aSample) +{ + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + +#if defined(DEBUG) + NS_ASSERTION(IsKeyframe(*aSample) == aSample->mKeyframe, + "AOM Decode Keyframe error sample->mKeyframe and si.si_kf out of sync"); +#endif + + if (aom_codec_err_t r = aom_codec_decode(&mCodec, aSample->Data(), aSample->Size(), nullptr, 0)) { + LOG_RESULT(r, "Decode error!"); + return DecodePromise::CreateAndReject( + MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("AOM error decoding AV1 sample: %s", + aom_codec_err_to_string(r))), + __func__); + } + + aom_codec_iter_t iter = nullptr; + aom_image_t *img; + DecodedData results; + + while ((img = aom_codec_get_frame(&mCodec, &iter))) { + NS_ASSERTION(img->fmt == AOM_IMG_FMT_I420 || + img->fmt == AOM_IMG_FMT_I444, + "WebM image format not I420 or I444"); + + // Chroma shifts are rounded down as per the decoding examples in the SDK + VideoData::YCbCrBuffer b; + b.mPlanes[0].mData = img->planes[0]; + b.mPlanes[0].mStride = img->stride[0]; + b.mPlanes[0].mHeight = img->d_h; + b.mPlanes[0].mWidth = img->d_w; + b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0; + + b.mPlanes[1].mData = img->planes[1]; + b.mPlanes[1].mStride = img->stride[1]; + b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0; + + b.mPlanes[2].mData = img->planes[2]; + b.mPlanes[2].mStride = img->stride[2]; + b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0; + + if (img->fmt == AOM_IMG_FMT_I420) { + b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift; + b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift; + + b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift; + b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift; + } else if (img->fmt == AOM_IMG_FMT_I444) { + b.mPlanes[1].mHeight = img->d_h; + b.mPlanes[1].mWidth = img->d_w; + + b.mPlanes[2].mHeight = img->d_h; + b.mPlanes[2].mWidth = img->d_w; + } else { + LOG("AOM Unknown image format"); + return DecodePromise::CreateAndReject( + MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("AOM Unknown image format")), + __func__); + } + + RefPtr<VideoData> v; + v = VideoData::CreateAndCopyData(mInfo, + mImageContainer, + aSample->mOffset, + aSample->mTime, + aSample->mDuration, + b, + aSample->mKeyframe, + aSample->mTimecode, + mInfo.ScaledImageRect(img->d_w, + img->d_h)); + + if (!v) { + LOG( + "Image allocation error source %ux%u display %ux%u picture %ux%u", + img->d_w, img->d_h, mInfo.mDisplay.width, mInfo.mDisplay.height, + mInfo.mImage.width, mInfo.mImage.height); + return DecodePromise::CreateAndReject( + MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__); + } + results.AppendElement(Move(v)); + } + return DecodePromise::CreateAndResolve(Move(results), __func__); +} + +RefPtr<MediaDataDecoder::DecodePromise> +AOMDecoder::Decode(MediaRawData* aSample) +{ + return InvokeAsync<MediaRawData*>(mTaskQueue, this, __func__, + &AOMDecoder::ProcessDecode, aSample); +} + +RefPtr<MediaDataDecoder::DecodePromise> +AOMDecoder::Drain() +{ + return InvokeAsync(mTaskQueue, __func__, [] { + return DecodePromise::CreateAndResolve(DecodedData(), __func__); + }); +} + + +/* static */ +bool +AOMDecoder::IsAV1(const nsACString& aMimeType) +{ + return aMimeType.EqualsLiteral("video/webm; codecs=av1") + || aMimeType.EqualsLiteral("video/av1"); +} + +/* static */ +bool +AOMDecoder::IsKeyframe(Span<const uint8_t> aBuffer) { + aom_codec_stream_info_t info; + PodZero(&info); + info.sz = sizeof(info); + + auto res = aom_codec_peek_stream_info(aom_codec_av1_dx(), + aBuffer.Elements(), + aBuffer.Length(), + &info); + if (res != AOM_CODEC_OK) { + LOG_RESULT(res, "couldn't get keyframe flag with aom_codec_peek_stream_info"); + return false; + } + + return bool(info.is_kf); +} + +/* static */ +nsIntSize +AOMDecoder::GetFrameSize(Span<const uint8_t> aBuffer) { + aom_codec_stream_info_t info; + PodZero(&info); + info.sz = sizeof(info); + + auto res = aom_codec_peek_stream_info(aom_codec_av1_dx(), + aBuffer.Elements(), + aBuffer.Length(), + &info); + if (res != AOM_CODEC_OK) { + LOG_RESULT(res, "couldn't get frame size with aom_codec_peek_stream_info"); + } + + return nsIntSize(info.w, info.h); +} + +} // namespace mozilla +#undef LOG
new file mode 100644 --- /dev/null +++ b/dom/media/platforms/agnostic/AOMDecoder.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ +#if !defined(AOMDecoder_h_) +#define AOMDecoder_h_ + +#include "PlatformDecoderModule.h" +#include "mozilla/Span.h" + +#include <stdint.h> +#include "aom/aom_decoder.h" + +namespace mozilla { + +class AOMDecoder : public MediaDataDecoder +{ +public: + explicit AOMDecoder(const CreateDecoderParams& aParams); + + RefPtr<InitPromise> Init() override; + RefPtr<DecodePromise> Decode(MediaRawData* aSample) override; + RefPtr<DecodePromise> Drain() override; + RefPtr<FlushPromise> Flush() override; + RefPtr<ShutdownPromise> Shutdown() override; + const char* GetDescriptionName() const override + { + return "libaom (AV1) video decoder"; + } + + // Return true if aMimeType is a one of the strings used + // by our demuxers to identify AV1 streams. + static bool IsAV1(const nsACString& aMimeType); + + // Return true if a sample is a keyframe. + static bool IsKeyframe(Span<const uint8_t> aBuffer); + + // Return the frame dimensions for a sample. + static nsIntSize GetFrameSize(Span<const uint8_t> aBuffer); + +private: + ~AOMDecoder(); + RefPtr<DecodePromise> ProcessDecode(MediaRawData* aSample); + + const RefPtr<layers::ImageContainer> mImageContainer; + const RefPtr<TaskQueue> mTaskQueue; + + // AOM decoder state + aom_codec_ctx_t mCodec; + + const VideoInfo& mInfo; +}; + +} // namespace mozilla + +#endif // AOMDecoder_h_
--- a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp +++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp @@ -7,42 +7,55 @@ #include "AgnosticDecoderModule.h" #include "OpusDecoder.h" #include "TheoraDecoder.h" #include "VPXDecoder.h" #include "VorbisDecoder.h" #include "WAVDecoder.h" #include "mozilla/Logging.h" +#ifdef MOZ_AV1 +#include "AOMDecoder.h" +#endif + namespace mozilla { bool AgnosticDecoderModule::SupportsMimeType( const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const { bool supports = VPXDecoder::IsVPX(aMimeType) +#ifdef MOZ_AV1 + || AOMDecoder::IsAV1(aMimeType) +#endif || OpusDataDecoder::IsOpus(aMimeType) || VorbisDataDecoder::IsVorbis(aMimeType) || WaveDataDecoder::IsWave(aMimeType) || TheoraDecoder::IsTheora(aMimeType); MOZ_LOG(sPDMLog, LogLevel::Debug, ("Agnostic decoder %s requested type", supports ? "supports" : "rejects")); return supports; } already_AddRefed<MediaDataDecoder> AgnosticDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) { RefPtr<MediaDataDecoder> m; if (VPXDecoder::IsVPX(aParams.mConfig.mMimeType)) { m = new VPXDecoder(aParams); - } else if (TheoraDecoder::IsTheora(aParams.mConfig.mMimeType)) { + } +#ifdef MOZ_AV1 + else if (AOMDecoder::IsAV1(aParams.mConfig.mMimeType)) { + m = new AOMDecoder(aParams); + } +#endif + else if (TheoraDecoder::IsTheora(aParams.mConfig.mMimeType)) { m = new TheoraDecoder(aParams); } return m.forget(); } already_AddRefed<MediaDataDecoder> AgnosticDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
--- a/dom/media/platforms/moz.build +++ b/dom/media/platforms/moz.build @@ -55,16 +55,24 @@ if CONFIG['MOZ_FFVPX']: 'ffmpeg/ffvpx', ] if CONFIG['MOZ_FFMPEG']: DIRS += [ 'ffmpeg', ] +if CONFIG['MOZ_AV1']: + EXPORTS += [ + 'agnostic/AOMDecoder.h', + ] + UNIFIED_SOURCES += [ + 'agnostic/AOMDecoder.cpp', + ] + if CONFIG['MOZ_APPLEMEDIA']: EXPORTS += [ 'apple/AppleDecoderModule.h', ] UNIFIED_SOURCES += [ 'apple/AppleATDecoder.cpp', 'apple/AppleCMLinker.cpp', 'apple/AppleDecoderModule.cpp',
--- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -3,16 +3,19 @@ /* 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 "nsError.h" #include "MediaDecoderStateMachine.h" #include "AbstractMediaDecoder.h" #include "MediaResource.h" +#ifdef MOZ_AV1 +#include "AOMDecoder.h" +#endif #include "OpusDecoder.h" #include "VPXDecoder.h" #include "WebMDemuxer.h" #include "WebMBufferedParser.h" #include "gfx2DGlue.h" #include "mozilla/Atomics.h" #include "mozilla/EndianUtils.h" #include "mozilla/SharedThreadPool.h" @@ -322,16 +325,19 @@ WebMDemuxer::ReadMetadata() mVideoCodec = nestegg_track_codec_id(context, track); switch(mVideoCodec) { case NESTEGG_CODEC_VP8: mInfo.mVideo.mMimeType = "video/webm; codecs=vp8"; break; case NESTEGG_CODEC_VP9: mInfo.mVideo.mMimeType = "video/webm; codecs=vp9"; break; + case NESTEGG_CODEC_AV1: + mInfo.mVideo.mMimeType = "video/webm; codecs=av1"; + break; default: NS_WARNING("Unknown WebM video codec"); return NS_ERROR_FAILURE; } // Picture region, taking into account cropping, before scaling // to the display size. unsigned int cropH = params.crop_right + params.crop_left; unsigned int cropV = params.crop_bottom + params.crop_top; @@ -677,27 +683,42 @@ WebMDemuxer::GetNextPacket(TrackInfo::Tr auto sample = MakeSpan(data, length); switch (mVideoCodec) { case NESTEGG_CODEC_VP8: isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP8); break; case NESTEGG_CODEC_VP9: isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP9); break; +#ifdef MOZ_AV1 + case NESTEGG_CODEC_AV1: + isKeyframe = AOMDecoder::IsKeyframe(sample); + break; +#endif default: NS_WARNING("Cannot detect keyframes in unknown WebM video codec"); return NS_ERROR_FAILURE; } if (isKeyframe) { // For both VP8 and VP9, we only look for resolution changes // on keyframes. Other resolution changes are invalid. - auto codec = mVideoCodec == NESTEGG_CODEC_VP8 - ? VPXDecoder::Codec::VP8 - : VPXDecoder::Codec::VP9; - auto dimensions = VPXDecoder::GetFrameSize(sample, codec); + auto dimensions = nsIntSize(0, 0); + switch (mVideoCodec) { + case NESTEGG_CODEC_VP8: + dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP8); + break; + case NESTEGG_CODEC_VP9: + dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP9); + break; +#ifdef MOZ_AV1 + case NESTEGG_CODEC_AV1: + dimensions = AOMDecoder::GetFrameSize(sample); + break; +#endif + } if (mLastSeenFrameSize.isSome() && (dimensions != mLastSeenFrameSize.value())) { mInfo.mVideo.mDisplay = dimensions; mSharedVideoTrackInfo = new TrackInfoSharedPtr(mInfo.mVideo, ++sStreamSourceID); } mLastSeenFrameSize = Some(dimensions); }
--- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -2704,16 +2704,23 @@ void RuntimeService::MemoryPressureAllWorkers() { BROADCAST_ALL_WORKERS(MemoryPressure, /* dummy = */ false); } uint32_t RuntimeService::ClampedHardwareConcurrency() const { + // The Firefox Hardware Report says 70% of Firefox users have exactly 2 cores. + // When the resistFingerprinting pref is set, we want to blend into the crowd + // so spoof navigator.hardwareConcurrency = 2 to reduce user uniqueness. + if (MOZ_UNLIKELY(nsContentUtils::ShouldResistFingerprinting())) { + return 2; + } + // This needs to be atomic, because multiple workers, and even mainthread, // could race to initialize it at once. static Atomic<uint32_t> clampedHardwareConcurrency; // No need to loop here: if compareExchange fails, that just means that some // other worker has initialized numberOfProcessors, so we're good to go. if (!clampedHardwareConcurrency) { int32_t numberOfProcessors = PR_GetNumberOfProcessors();
--- a/dom/workers/test/test_navigator_workers_hardwareConcurrency.html +++ b/dom/workers/test/test_navigator_workers_hardwareConcurrency.html @@ -1,27 +1,53 @@ <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Test for Navigator.hardwareConcurrency</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="application/javascript"> + "use strict"; SimpleTest.waitForExplicitFinish(); - var script = "postMessage(navigator.hardwareConcurrency)"; - var url = URL.createObjectURL(new Blob([script])); - var w = new Worker(url); - w.onmessage = function(e) { + + function getWorkerHardwareConcurrency(onmessage) { + var script = "postMessage(navigator.hardwareConcurrency)"; + var url = URL.createObjectURL(new Blob([script])); + var w = new Worker(url); + w.onmessage = onmessage; + } + + function resistFingerprinting(value) { + return SpecialPowers.pushPrefEnv({"set": [["privacy.resistFingerprinting", value]]}); + } + + getWorkerHardwareConcurrency(e => { var x = e.data; is(typeof x, "number", "hardwareConcurrency should be a number."); ok(x > 0, "hardwareConcurrency should be greater than 0."); - SimpleTest.finish(); - } + + resistFingerprinting(true).then(() => { + getWorkerHardwareConcurrency(e => { + const y = e.data; + ok(y === 2, "hardwareConcurrency should always be 2 when we're resisting fingerprinting."); + + resistFingerprinting(false).then(() => { + getWorkerHardwareConcurrency(e => { + const z = e.data; + ok(z === x, "hardwareConcurrency should be the same as before we were resisting fingerprinting."); + + SimpleTest.finish(); + }); + }); + }); + }); + }); + </script> </head> <body> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test">
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7311,16 +7311,20 @@ BytecodeEmitter::emitForOf(ParseNode* fo JumpList beq; JumpTarget breakTarget{ -1 }; { #ifdef DEBUG auto loopDepth = this->stackDepth; #endif + // Make sure this code is attributed to the "for". + if (!updateSourceCoordNotes(forOfHead->pn_pos.begin)) + return false; + if (!emit1(JSOP_POP)) // ITER return false; if (!emit1(JSOP_DUP)) // ITER ITER return false; if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) return false; // ITER RESULT @@ -7519,16 +7523,20 @@ BytecodeEmitter::emitForIn(ParseNode* fo // Perform the loop body. ParseNode* forBody = forInLoop->pn_right; if (!emitTree(forBody)) // ITER ITERVAL return false; // Set offset for continues. loopInfo.continueTarget = { offset() }; + // Make sure this code is attributed to the "for". + if (!updateSourceCoordNotes(forInHead->pn_pos.begin)) + return false; + if (!emitLoopEntry(nullptr, initialJump)) // ITER ITERVAL return false; if (!emit1(JSOP_POP)) // ITER return false; if (!emit1(JSOP_MOREITER)) // ITER NEXTITERVAL? return false; if (!emit1(JSOP_ISNOITER)) // ITER NEXTITERVAL? ISNOITER return false;
--- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -459,16 +459,23 @@ JS_GetTraceThingInfo(char* buf, size_t b snprintf(buf, bufsize, "<nonlinear desc>"); } } else { snprintf(buf, bufsize, "<null>"); } break; } + case JS::TraceKind::Scope: + { + js::Scope* scope = static_cast<js::Scope*>(thing); + snprintf(buf, bufsize, " %s", js::ScopeKindString(scope->kind())); + break; + } + default: break; } } buf[bufsize - 1] = '\0'; } JS::CallbackTracer::CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind)
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/debug/Frame-onStep-17.js @@ -0,0 +1,31 @@ +var g = newGlobal(); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +var log; +var previous; + +dbg.onDebuggerStatement = function (frame) { + let debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber; + log = ''; + previous = ''; + frame.onStep = function() { + let foundLine = this.script.getOffsetLocation(this.offset).lineNumber; + if (this.script.getLineOffsets(foundLine).indexOf(this.offset) >= 0) { + let thisline = (foundLine - debugLine).toString(16); + if (thisline !== previous) { + log += thisline; + previous = thisline; + } + } + }; +}; + +function testOne(loopKind) { + let body = "var array = [2, 4, 6];\ndebugger;\nfor (let iter " + + loopKind + " array) {\n print(iter);\n}\n"; + g.eval(body); + assertEq(log, "12121212"); +} + +testOne("in"); +testOne("of");
--- a/layout/base/nsCounterManager.cpp +++ b/layout/base/nsCounterManager.cpp @@ -44,20 +44,18 @@ nsCounterUseNode::InitTextFrame(nsGenCon } CounterStyle* nsCounterUseNode::GetCounterStyle() { if (!mCounterStyle) { const nsCSSValue& style = mCounterFunction->Item(mAllCounters ? 2 : 1); CounterStyleManager* manager = mPresContext->CounterStyleManager(); - if (style.GetUnit() == eCSSUnit_Ident) { - nsString ident; - style.GetStringValue(ident); - mCounterStyle = manager->BuildCounterStyle(ident); + if (style.GetUnit() == eCSSUnit_AtomIdent) { + mCounterStyle = manager->BuildCounterStyle(style.GetAtomValue()); } else if (style.GetUnit() == eCSSUnit_Symbols) { mCounterStyle = new AnonymousCounterStyle(style.GetArrayValue()); } else { NS_NOTREACHED("Unknown counter style"); mCounterStyle = CounterStyleManager::GetDecimalStyle(); } } return mCounterStyle;
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -25,17 +25,16 @@ #include "nsDocument.h" #include "nsFontMetrics.h" #include "nsPresContext.h" #include "nsIContent.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLElement.h" #include "nsFrameList.h" #include "nsGkAtoms.h" -#include "nsHtml5Atoms.h" #include "nsIAtom.h" #include "nsCaret.h" #include "nsCSSPseudoElements.h" #include "nsCSSAnonBoxes.h" #include "nsCSSColorUtils.h" #include "nsView.h" #include "nsViewManager.h" #include "nsPlaceholderFrame.h" @@ -117,16 +116,17 @@ #include "mozilla/EventDispatcher.h" #include "mozilla/EventStateManager.h" #include "mozilla/RuleNodeCacheConditions.h" #include "mozilla/StyleAnimationValue.h" #include "mozilla/StyleSetHandle.h" #include "mozilla/StyleSetHandleInlines.h" #include "RegionBuilder.h" #include "SVGSVGElement.h" +#include "DisplayItemClip.h" #ifdef MOZ_XUL #include "nsXULPopupManager.h" #endif #include "GeckoProfiler.h" #include "nsAnimationManager.h" #include "nsTransitionManager.h" @@ -8933,26 +8933,33 @@ nsLayoutUtils::GetTouchActionFromFrame(n /* static */ void nsLayoutUtils::TransformToAncestorAndCombineRegions( const nsRegion& aRegion, nsIFrame* aFrame, const nsIFrame* aAncestorFrame, nsRegion* aPreciseTargetDest, nsRegion* aImpreciseTargetDest, - Maybe<Matrix4x4>* aMatrixCache) + Maybe<Matrix4x4>* aMatrixCache, + const DisplayItemClip* aClip) { if (aRegion.IsEmpty()) { return; } bool isPrecise; RegionBuilder<nsRegion> transformedRegion; for (nsRegion::RectIterator it = aRegion.RectIter(); !it.Done(); it.Next()) { nsRect transformed = TransformFrameRectToAncestor( aFrame, it.Get(), aAncestorFrame, &isPrecise, aMatrixCache); + if (aClip) { + transformed = aClip->ApplyNonRoundedIntersection(transformed); + if (aClip->GetRoundedRectCount() > 0) { + isPrecise = false; + } + } transformedRegion.OrWith(transformed); } nsRegion* dest = isPrecise ? aPreciseTargetDest : aImpreciseTargetDest; dest->OrWith(transformedRegion.ToRegion()); } /* static */ bool nsLayoutUtils::ShouldUseNoScriptSheet(nsIDocument* aDocument)
--- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -65,16 +65,17 @@ struct nsOverflowAreas; namespace mozilla { enum class CSSPseudoElementType : uint8_t; class EventListenerManager; enum class LayoutFrameType : uint8_t; struct IntrinsicSize; struct ContainerLayerParameters; class WritingMode; +class DisplayItemClip; namespace dom { class CanvasRenderingContext2D; class DOMRectList; class Element; class HTMLImageElement; class HTMLCanvasElement; class HTMLVideoElement; class OffscreenCanvas; @@ -2584,26 +2585,29 @@ public: * Helper method to get touch action behaviour from the frame */ static uint32_t GetTouchActionFromFrame(nsIFrame* aFrame); /** * Helper method to transform |aBounds| from aFrame to aAncestorFrame, * and combine it with |aPreciseTargetDest| if it is axis-aligned, or - * combine it with |aImpreciseTargetDest| if not. + * combine it with |aImpreciseTargetDest| if not. The transformed rect is + * clipped to |aClip|; if |aClip| has rounded corners, that also causes + * the imprecise target to be used. */ static void TransformToAncestorAndCombineRegions( const nsRegion& aRegion, nsIFrame* aFrame, const nsIFrame* aAncestorFrame, nsRegion* aPreciseTargetDest, nsRegion* aImpreciseTargetDest, - mozilla::Maybe<Matrix4x4>* aMatrixCache); + mozilla::Maybe<Matrix4x4>* aMatrixCache, + const mozilla::DisplayItemClip* aClip); /** * Populate aOutSize with the size of the content viewer corresponding * to the given prescontext. Return true if the size was set, false * otherwise. */ static bool GetContentViewerSize(nsPresContext* aPresContext,
--- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -46,17 +46,16 @@ #include "nsTreeSanitizer.h" #include "nsCellMap.h" #include "nsTextFrame.h" #include "nsCCUncollectableMarker.h" #include "nsTextFragment.h" #include "nsCSSRuleProcessor.h" #include "nsCORSListenerProxy.h" #include "nsHTMLDNSPrefetch.h" -#include "nsHtml5Atoms.h" #include "nsHtml5Module.h" #include "nsHTMLTags.h" #include "nsIRDFContentSink.h" // for RDF atom initialization #include "mozilla/dom/FallbackEncoding.h" #include "nsFocusManager.h" #include "nsListControlFrame.h" #include "mozilla/dom/HTMLInputElement.h" #include "SVGElementFactory.h" @@ -153,17 +152,16 @@ nsLayoutStatics::Initialize() // Register all of our atoms once nsCSSAnonBoxes::AddRefAtoms(); nsCSSPseudoClasses::AddRefAtoms(); nsCSSPseudoElements::AddRefAtoms(); nsCSSKeywords::AddRefTable(); nsCSSProps::AddRefTable(); nsColorNames::AddRefTable(); nsGkAtoms::AddRefAtoms(); - nsHtml5Atoms::AddRefAtoms(); nsTextServicesDocument::RegisterAtoms(); nsHTMLTags::RegisterAtoms(); nsRDFAtoms::RegisterAtoms(); NS_SealStaticAtomTable(); StartupJSEnvironment(); rv = nsRegion::InitStatic();
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -46,17 +46,16 @@ #include "mozilla/RestyleManager.h" #include "mozilla/RestyleManagerInlines.h" #include "nsIDOMNode.h" #include "nsISelection.h" #include "nsISelectionPrivate.h" #include "nsFrameSelection.h" #include "nsGkAtoms.h" -#include "nsHtml5Atoms.h" #include "nsCSSAnonBoxes.h" #include "nsFrameTraversal.h" #include "nsRange.h" #include "nsITextControlFrame.h" #include "nsNameSpaceManager.h" #include "nsIPercentBSizeObserver.h" #include "nsStyleStructInlines.h"
--- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -113,16 +113,18 @@ static inline MaskLayerImageCache* GetMa gMaskLayerImageCache = new MaskLayerImageCache(); } return gMaskLayerImageCache; } FrameLayerBuilder::FrameLayerBuilder() : mRetainingManager(nullptr) + , mContainingPaintedLayer(nullptr) + , mInactiveLayerClip(nullptr) , mDetectedDOMModification(false) , mInvalidateAllLayers(false) , mInLayerTreeCompressionMode(false) , mContainerLayerGeneration(0) , mMaxContainerLayerGeneration(0) { MOZ_COUNT_CTOR(FrameLayerBuilder); } @@ -1783,24 +1785,26 @@ FrameLayerBuilder::Shutdown() if (gMaskLayerImageCache) { delete gMaskLayerImageCache; gMaskLayerImageCache = nullptr; } } void FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager, - PaintedLayerData* aLayerData) + PaintedLayerData* aLayerData, + const DisplayItemClip* aInactiveLayerClip) { mDisplayListBuilder = aBuilder; mRootPresContext = aBuilder->RootReferenceFrame()->PresContext()->GetRootPresContext(); if (mRootPresContext) { mInitialDOMGeneration = mRootPresContext->GetDOMGeneration(); } mContainingPaintedLayer = aLayerData; + mInactiveLayerClip = aInactiveLayerClip; aManager->SetUserData(&gLayerManagerLayerBuilder, this); } void FrameLayerBuilder::FlashPaint(gfxContext *aContext) { float r = float(rand()) / RAND_MAX; float g = float(rand()) / RAND_MAX; @@ -3314,69 +3318,88 @@ void ContainerState::FinishPaintedLayerD } if (data->mDisableFlattening) { flags |= Layer::CONTENT_DISABLE_FLATTENING; } layer->SetContentFlags(flags); PaintedLayerData* containingPaintedLayerData = mLayerBuilder->GetContainingPaintedLayerData(); + // If we're building layers for an inactive layer, the event regions are + // clipped to the inactive layer's clip prior to being combined into the + // event regions of the containing PLD. + // For the dispatch-to-content and maybe-hit regions, rounded corners on + // the clip are ignored, since these are approximate regions. For the + // remaining regions, rounded corners in the clip cause the region to + // be combined into the corresponding "imprecise" region of the + // containing's PLD (e.g. the maybe-hit region instead of the hit region). + const DisplayItemClip* inactiveLayerClip = mLayerBuilder->GetInactiveLayerClip(); if (containingPaintedLayerData) { if (!data->mDispatchToContentHitRegion.GetBounds().IsEmpty()) { nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor( mContainerReferenceFrame, data->mDispatchToContentHitRegion.GetBounds(), containingPaintedLayerData->mReferenceFrame); + if (inactiveLayerClip) { + rect = inactiveLayerClip->ApplyNonRoundedIntersection(rect); + } containingPaintedLayerData->mDispatchToContentHitRegion.Or( containingPaintedLayerData->mDispatchToContentHitRegion, rect); } if (!data->mMaybeHitRegion.GetBounds().IsEmpty()) { nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor( mContainerReferenceFrame, data->mMaybeHitRegion.GetBounds(), containingPaintedLayerData->mReferenceFrame); + if (inactiveLayerClip) { + rect = inactiveLayerClip->ApplyNonRoundedIntersection(rect); + } containingPaintedLayerData->mMaybeHitRegion.Or( containingPaintedLayerData->mMaybeHitRegion, rect); containingPaintedLayerData->mMaybeHitRegion.SimplifyOutward(8); } Maybe<Matrix4x4> matrixCache; nsLayoutUtils::TransformToAncestorAndCombineRegions( data->mHitRegion, mContainerReferenceFrame, containingPaintedLayerData->mReferenceFrame, &containingPaintedLayerData->mHitRegion, &containingPaintedLayerData->mMaybeHitRegion, - &matrixCache); + &matrixCache, + inactiveLayerClip); // See the comment in nsDisplayList::AddFrame, where the touch action regions // are handled. The same thing applies here. bool alreadyHadRegions = !containingPaintedLayerData->mNoActionRegion.IsEmpty() || !containingPaintedLayerData->mHorizontalPanRegion.IsEmpty() || !containingPaintedLayerData->mVerticalPanRegion.IsEmpty(); nsLayoutUtils::TransformToAncestorAndCombineRegions( data->mNoActionRegion, mContainerReferenceFrame, containingPaintedLayerData->mReferenceFrame, &containingPaintedLayerData->mNoActionRegion, &containingPaintedLayerData->mDispatchToContentHitRegion, - &matrixCache); + &matrixCache, + inactiveLayerClip); nsLayoutUtils::TransformToAncestorAndCombineRegions( data->mHorizontalPanRegion, mContainerReferenceFrame, containingPaintedLayerData->mReferenceFrame, &containingPaintedLayerData->mHorizontalPanRegion, &containingPaintedLayerData->mDispatchToContentHitRegion, - &matrixCache); + &matrixCache, + inactiveLayerClip); nsLayoutUtils::TransformToAncestorAndCombineRegions( data->mVerticalPanRegion, mContainerReferenceFrame, containingPaintedLayerData->mReferenceFrame, &containingPaintedLayerData->mVerticalPanRegion, &containingPaintedLayerData->mDispatchToContentHitRegion, - &matrixCache); + &matrixCache, + inactiveLayerClip); if (alreadyHadRegions) { containingPaintedLayerData->mDispatchToContentHitRegion.OrWith( containingPaintedLayerData->CombinedTouchActionRegion()); } } else { EventRegions regions; regions.mHitRegion = ScaleRegionToOutsidePixels(data->mHitRegion); regions.mNoActionRegion = ScaleRegionToOutsidePixels(data->mNoActionRegion); @@ -4659,17 +4682,17 @@ FrameLayerBuilder::AddPaintedDisplayItem if (entry) { entry->mContainerLayerFrame = aContainerState.GetContainerFrame(); if (entry->mContainerLayerGeneration == 0) { entry->mContainerLayerGeneration = mContainerLayerGeneration; } if (tempManager) { FLB_LOG_PAINTED_LAYER_DECISION(aLayerData, "Creating nested FLB for item %p\n", aItem); FrameLayerBuilder* layerBuilder = new FrameLayerBuilder(); - layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData); + layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData, &aClip); tempManager->BeginTransaction(); if (mRetainingManager) { layerBuilder->DidBeginRetainedLayerTransaction(tempManager); } UniquePtr<LayerProperties> props(LayerProperties::CloneFrom(tempManager->GetRoot())); RefPtr<Layer> tmpLayer =
--- a/layout/painting/FrameLayerBuilder.h +++ b/layout/painting/FrameLayerBuilder.h @@ -194,17 +194,18 @@ public: typedef layers::EventRegions EventRegions; FrameLayerBuilder(); ~FrameLayerBuilder(); static void Shutdown(); void Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager, - PaintedLayerData* aLayerData = nullptr); + PaintedLayerData* aLayerData = nullptr, + const DisplayItemClip* aInactiveLayerClip = nullptr); /** * Call this to notify that we have just started a transaction on the * retained layer manager aManager. */ void DidBeginRetainedLayerTransaction(LayerManager* aManager); /** @@ -688,16 +689,21 @@ public: return mPaintedLayerItems.GetEntry(aLayer); } PaintedLayerData* GetContainingPaintedLayerData() { return mContainingPaintedLayer; } + const DisplayItemClip* GetInactiveLayerClip() const + { + return mInactiveLayerClip; + } + bool IsBuildingRetainedLayers() { return !mContainingPaintedLayer && mRetainingManager; } /** * Attempt to build the most compressed layer tree possible, even if it means * throwing away existing retained buffers. @@ -737,16 +743,22 @@ protected: /** * When building layers for an inactive layer, this is where the * inactive layer will be placed. */ PaintedLayerData* mContainingPaintedLayer; /** + * When building layers for an inactive layer, this stores the clip + * of the display item that built the inactive layer. + */ + const DisplayItemClip* mInactiveLayerClip; + + /** * Saved generation counter so we can detect DOM changes. */ uint32_t mInitialDOMGeneration; /** * Set to true if we have detected and reported DOM modification during * the current paint. */ bool mDetectedDOMModification;
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -910,17 +910,17 @@ fuzzy-if(Android,8,50) fails-if(stylo) = == 405517-1.xhtml 405517-1-ref.xhtml == 405577-1.html 405577-1-ref.html == 405584-1.html 405584-1-ref.html # == 405952-1.html 405952-1-ref.html == 406484-1.html 406484-1-ref.html == 406568-1.html 406568-1-ref.html fails-if(stylo) == 407016-1-a.html 407016-1-ref.html fails-if(stylo) == 407016-1-b.html 407016-1-ref.html -fails-if(stylo) == 407078-1.html 407078-1-ref.html +== 407078-1.html 407078-1-ref.html == 407095-1.html 407095-1-ref.html fuzzy-if(Android,13,9) == 407111-1.html 407111-1-ref.html # Bug 1128229 == 407227-1.html 407227-1-ref.html == 407243-1.html 407243-1-ref.html == 407419-1.html 407419-1-ref.html == 407937-1.html 407937-1-ref.html == 408493-1.html about:blank == 408493-2.html 408493-2-ref.html @@ -1499,17 +1499,17 @@ fuzzy-if(Android,12,300) == 551463-1.htm # behavior further in the future, which could make this become relevant again. # Marked "random" rather than "fails" because it may (spuriously) appear to pass # on Android devices that completely lack any Sinhala font support. random != 553571-1.html 553571-1-notref.html # expect dotted circle in test, not in ref: "fails" under harfbuzz, which doesn't consider the sequence invalid fuzzy-if(!contentSameGfxBackendAsCanvas,128,91) random-if(d2d) skip-if(azureSkiaGL) fuzzy-if(skiaContent,32,150) == 555388-1.html 555388-1-ref.html == 556661-1.html 556661-1-ref.html fuzzy-if(skiaContent,4,5) fails-if(stylo) == 557087-1.html 557087-ref.html fails-if(Android) fuzzy-if(skiaContent&&!Android,2,5) fails-if(stylo) == 557087-2.html 557087-ref.html -fails-if(stylo) == 557736-1.html 557736-1-ref.html +== 557736-1.html 557736-1-ref.html != 558011-1.xul 558011-1-ref.xul == 559284-1.html 559284-1-ref.html fails-if(Android) == 560455-1.xul 560455-1-ref.xul fuzzy-if(skiaContent,2,5) == 561981-1.html 561981-1-ref.html == 561981-2.html 561981-2-ref.html fuzzy-if(skiaContent,1,5) == 561981-3.html 561981-3-ref.html == 561981-4.html 561981-4-ref.html fuzzy-if(skiaContent,1,5) == 561981-5.html 561981-5-ref.html
--- a/layout/reftests/font-face/reftest.list +++ b/layout/reftests/font-face/reftest.list @@ -8,18 +8,18 @@ HTTP(..) != download-2.html about:blank random-if(winWidget||gtkWidget) HTTP(..) == download-2-big.html download-2-big-otf.html # bug 470713 HTTP(..) != download-2-big-otf.html about:blank asserts-if(Android&&!asyncPan,1-8) fails-if(stylo) HTTP(..) != download-3-notref.html download-3.html # bug 1019192 asserts-if(Android,0-8) fails-if(stylo) HTTP(..) == download-3-ref.html download-3.html # same bugs as above asserts-if(Android,0-8) HTTP(..) == fallback-to-system-1.html fallback-to-system-1-ref.html # just delayed assertions from above tests HTTP(..) == name-override-simple-1.html name-override-simple-1-ref.html HTTP(..) != name-override-simple-1.html download-1-notref.html fails-if(!stylo) HTTP(..) == name-override-1.html name-override-1-ref.html -fails-if(stylo) HTTP(..) == multiple-descriptor-1.html multiple-descriptor-1-ref.html -fails-if(stylo) HTTP(..) != multiple-descriptor-1.html multiple-descriptor-1-notref.html +HTTP(..) == multiple-descriptor-1.html multiple-descriptor-1-ref.html +HTTP(..) != multiple-descriptor-1.html multiple-descriptor-1-notref.html HTTP(..) == src-list-1.html src-list-1-ref.html HTTP(..) == src-list-2.html src-list-2-ref.html random-if(winWidget||gtkWidget) HTTP(..) == src-list-2-big-otf.html src-list-2-big-ref.html # bug 470713 HTTP(..) == src-list-format-1.html src-list-format-1-ref.html HTTP(..) == src-list-format-2.html src-list-format-2-ref.html HTTP(..) == src-list-format-3.html src-list-format-3-ref.html HTTP(..) == src-list-format-4.html src-list-format-1-ref.html HTTP(..) == src-list-format-5.html src-list-format-2-ref.html @@ -53,17 +53,17 @@ HTTP(..) == order-1.html order-1-ref.htm HTTP(..) == order-2.html order-2-ref.html HTTP(..) == order-3.html order-3-ref.html HTTP(..) == multiple-in-family-1.html multiple-in-family-1-ref.html HTTP(..) == multiple-in-family-1b.html multiple-in-family-1-ref.html HTTP(..) != multiple-in-family-1.html multiple-in-family-1-notref.html HTTP(..) == prop-order-over-rule-order-1a.html prop-order-over-rule-order-2a.html HTTP(..) == prop-order-over-rule-order-1b.html prop-order-over-rule-order-2b.html HTTP(..) != prop-order-over-rule-order-1a.html prop-order-over-rule-order-1b.html -fails-if(stylo) HTTP(..) == cross-iframe-1.html cross-iframe-1-ref.html +HTTP(..) == cross-iframe-1.html cross-iframe-1-ref.html # unicode-range HTTP(..) == unicoderange-1.html unicoderange-1-ref.html HTTP(..) == unicoderange-2.html unicoderange-2-ref.html HTTP(..) == unicoderange-3.html unicoderange-3-ref.html HTTP(..) == unicoderange-4.html unicoderange-4-ref.html # Dynamic changes
--- a/layout/reftests/font-features/reftest.list +++ b/layout/reftests/font-features/reftest.list @@ -3,63 +3,63 @@ # These rely on the Linux Libertine font (loaded via @font-face) # to ensure that features are present. # check that Turkish language causes a change in rendering (no fi ligature) # (also works via Pango) HTTP(..) != font-features-turkish.html font-features-ref.html # check that disabling ligatures causes a change -fails-if(stylo) HTTP(..) != font-features-noliga.html font-features-ref.html +HTTP(..) != font-features-noliga.html font-features-ref.html # check that enabling optional ligatures causes a change -fails-if(stylo) HTTP(..) != font-features-hlig.html font-features-ref.html +HTTP(..) != font-features-hlig.html font-features-ref.html # compare Turkish rendering with reference using ZWNJ to break the ligature HTTP(..) == font-features-turkish.html font-features-turkish-ref.html # compare Turkish rendering with explicitly disabled ligatures HTTP(..) == font-features-turkish.html font-features-noliga.html # The following should pass even if feature support isn't available, # because both testcase and reference will have the default rendering, # though they're not really meaningful unless the tests above passed already. # compare feature specified within @font-face to same feature in style rule fails-if(stylo) HTTP(..) == font-features-hlig-2.html font-features-hlig.html -fails-if(stylo) HTTP(..) == font-features-hlig-4.html font-features-hlig.html +HTTP(..) == font-features-hlig-4.html font-features-hlig.html HTTP(..) != font-features-hlig-5.html font-features-hlig.html HTTP(..) == font-features-ligatures-none.html font-features-noliga.html # check that feature in style rule overrides @font-face -fails-if(stylo) HTTP(..) == font-features-hlig-3.html font-features-noliga.html +HTTP(..) == font-features-hlig-3.html font-features-noliga.html # compare font-language-override rendering to lang-tagged rendering HTTP(..) == font-features-turkish-override-1.html font-features-turkish.html fails-if(stylo) HTTP(..) == font-features-turkish-override-2.html font-features-turkish.html # check use of font-language-override to override explicit lang tag HTTP(..) == font-features-turkish-override-3.html font-features-ref.html fails-if(stylo) HTTP(..) == font-features-turkish-override-4.html font-features-ref.html HTTP(..) == font-features-turkish-override-5.html font-features-turkish.html # check that last value wins if a feature is repeated HTTP(..) == font-features-order-1.html font-features-ref.html -fails-if(stylo) HTTP(..) == font-features-order-2.html font-features-noliga.html +HTTP(..) == font-features-order-2.html font-features-noliga.html # check priority of feature settings vs. font-variant subproperty -fails-if(stylo) HTTP(..) == font-features-order-3.html font-features-noliga.html +HTTP(..) == font-features-order-3.html font-features-noliga.html HTTP(..) == font-features-order-4.html font-features-noliga.html -fails-if(stylo) HTTP(..) == font-features-order-5.html font-features-hlig.html +HTTP(..) == font-features-order-5.html font-features-hlig.html # check priority involving feature settings and font-variant-alternates fails-if(stylo) HTTP(..) == alternates-order.html alternates-order-ref.html # check that font-specific values line up with @font-face feature settings -fails-if(stylo) HTTP(..) == annotations.html annotations-ref.html +HTTP(..) == annotations.html annotations-ref.html # font-variant subproperties # test for specific features being on and others off, based on prop values # (debug problems with font-variant-debug.html which displays all props) fails-if(stylo) HTTP(..) == font-variant-alternates.html font-variant-alternates-ref.html fails-if(stylo) HTTP(..) == font-variant-caps.html font-variant-caps-ref.html HTTP(..) == font-variant-east-asian.html font-variant-east-asian-ref.html HTTP(..) == font-variant-ligatures.html font-variant-ligatures-ref.html @@ -67,37 +67,37 @@ HTTP(..) == font-variant-numeric.html fo HTTP(..) == font-variant-position.html font-variant-position-ref.html # font-kerning HTTP(..) != font-kerning-normal.html font-kerning-none.html HTTP(..) != font-kerning-auto.html font-kerning-none.html HTTP(..) == font-kerning-auto.html font-kerning-normal.html HTTP(..) == font-kerning-normal.html font-kerning-kern.html HTTP(..) == font-kerning-none.html font-kerning-nokern.html -fails-if(stylo) HTTP(..) == font-kerning-1.html font-kerning-none.html -fails-if(stylo) HTTP(..) == font-kerning-2.html font-kerning-normal.html +HTTP(..) == font-kerning-1.html font-kerning-none.html +HTTP(..) == font-kerning-2.html font-kerning-normal.html fails-if(stylo) HTTP(..) == font-kerning-3.html font-kerning-none.html HTTP(..) != font-kerning-table-none.html font-kerning-table-normal.html # sanity check for kerning - with no spaces, kerning should occur HTTP(..) == kerning-sanity-check-kern.html kerning-sanity-check-default.html -fails-if(stylo) HTTP(..) != kerning-sanity-check-nokern.html kerning-sanity-check-default.html +HTTP(..) != kerning-sanity-check-nokern.html kerning-sanity-check-default.html # OpenType features should work across inter-word spaces HTTP(..) == font-features-across-space-1.html font-features-across-space-1-ref.html -fails-if(stylo) HTTP(..) == spacelookups.html spacelookups-ref.html +HTTP(..) == spacelookups.html spacelookups-ref.html # tests whether word cache is in use by testing for ignored space kerns HTTP(..) == spacelookups-wordcache.html spacelookups-wordcache-ref.html # requires Japanese font with feature support, WinXP lacks one random-if(!winWidget&&!cocoaWidget) HTTP(..) == fwid-spaces.html fwid-spaces-ref.html # Arial/Times New Roman on Win7+/OSX 10.6+ have kerning pairs that include spaces random-if(!winWidget&&!cocoaWidget) fails-if(winWidget||cocoaWidget) HTTP(..) != kerning-spaces-arial-nokern.html kerning-spaces-arial-default.html -random-if(!winWidget&&!cocoaWidget) fails-if(winWidget||cocoaWidget) fails-if(stylo) HTTP(..) == kerning-spaces-arial-kern.html kerning-spaces-arial-default.html +random-if(!winWidget&&!cocoaWidget) fails-if(winWidget||cocoaWidget) HTTP(..) == kerning-spaces-arial-kern.html kerning-spaces-arial-default.html random-if(!winWidget&&!cocoaWidget) fails-if(winWidget||cocoaWidget) HTTP(..) != kerning-spaces-tnr-nokern.html kerning-spaces-tnr-default.html -random-if(!winWidget&&!cocoaWidget) fails-if(winWidget||cocoaWidget) fails-if(stylo) HTTP(..) == kerning-spaces-tnr-kern.html kerning-spaces-tnr-default.html +random-if(!winWidget&&!cocoaWidget) fails-if(winWidget||cocoaWidget) HTTP(..) == kerning-spaces-tnr-kern.html kerning-spaces-tnr-default.html # font-variant-caps fallback # -- sanity check - none of these should look like the default rendering HTTP(..) != caps-fallback-smallcaps1.html caps-fallback-default.html HTTP(..) != caps-fallback-smallcaps2.html caps-fallback-default.html HTTP(..) != caps-fallback-petitecaps.html caps-fallback-default.html fails-if(stylo) HTTP(..) != caps-fallback-allsmallcaps.html caps-fallback-default.html fails-if(stylo) HTTP(..) != caps-fallback-allpetitecaps.html caps-fallback-default.html
--- a/layout/reftests/font-inflation/reftest.list +++ b/layout/reftests/font-inflation/reftest.list @@ -64,17 +64,17 @@ test-pref(font.size.inflation.emPerLine, test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) != disable-fontinfl-on-mobile-5.html disable-fontinfl-on-mobile-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == preformatted-text.html preformatted-text-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == fixed-height-body.html fixed-height-body-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == fixed-height-body-child.html fixed-height-body-child-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == consecutive-inline.html consecutive-inline-ref.html # The tests below use nonzero values of the lineThreshold preference. test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == text-1.html text-1.html -test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) fails-if(stylo) HTTP(..) == list-1.html list-1-ref.html +test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) HTTP(..) == list-1.html list-1-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-1a.html threshold-1a.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-1b.html threshold-1b-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-1c.html threshold-1c-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-2.html threshold-2-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-3.html threshold-3-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-scope-float-1.html threshold-scope-float-1-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-scope-float-2.html threshold-scope-float-2-ref.html test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-scope-float-overflow-1.html threshold-scope-float-overflow-1-ref.html
--- a/layout/reftests/font-matching/reftest.list +++ b/layout/reftests/font-matching/reftest.list @@ -1,9 +1,9 @@ -fails-if(stylo) == CSS21-t1502-no-inherited-font-family.xhtml CSS21-t1502-no-inherited-font-family-ref.xhtml +== CSS21-t1502-no-inherited-font-family.xhtml CSS21-t1502-no-inherited-font-family-ref.xhtml # basic tests for bug 538730 != synthetic-bold-1.html synthetic-bold-1-ref.html != synthetic-bold-2.html synthetic-bold-2-ref.html # synthetic bold/italic tests != defaultfont-bold.html defaultfont.html != defaultfont-italic.html defaultfont.html @@ -39,20 +39,20 @@ random-if(cocoaWidget) != impact-bold.ht == arial-variations-1.html arial-variations-1-ref.html == arial-variations-2.html arial-variations-2-ref.html == arial-variations-3.html arial-variations-3-ref.html == arial-variations-4.html arial-variations-4-ref.html == arial-variations-5.html arial-variations-5-ref.html == arial-variations-6.html arial-variations-6-ref.html # localized font family names should always match just as English names do -fails-if(stylo) == localized-family-names-001.html localized-family-names-001-ref.html -fails-if(stylo) == localized-family-names-002.html localized-family-names-002-ref.html -fails-if(stylo) == localized-family-names-003.html localized-family-names-003-ref.html -fails-if(stylo) == localized-family-names-004.html localized-family-names-004-ref.html +== localized-family-names-001.html localized-family-names-001-ref.html +== localized-family-names-002.html localized-family-names-002-ref.html +== localized-family-names-003.html localized-family-names-003-ref.html +== localized-family-names-004.html localized-family-names-004-ref.html # family names with escaped spaces shouldn't match the names without the spaces fails-if(gtkWidget&&!stylo) == familyname-escapedidents.html familyname-escapedidents-ref.html # bug 1309425, bug 1328771 # weight mapping tests HTTP(..) == normalmedium.html normalmedium-ref.html HTTP(..) != normalmedium.html normalmedium-notref.html
--- a/layout/reftests/mathml/reftest.list +++ b/layout/reftests/mathml/reftest.list @@ -288,25 +288,25 @@ fails-if(winWidget) == subscript-italic- fails-if(Android) == mathvariant-1a.html mathvariant-1a-ref.html # Bug 1010679 fails-if(Android) == mathvariant-1b.html mathvariant-1b-ref.html # Bug 1010679 fails-if(Android) == mathvariant-1c.html mathvariant-1c-ref.html # Bug 1010679 == mathvariant-1d.html mathvariant-1d-ref.html fails-if(Android||OSX) == mathvariant-2.html mathvariant-2-ref.html # Bugs 1010678, 1010679 fails-if(stylo) == mathvariant-3.html mathvariant-3-ref.html == mathvariant-4.html mathvariant-4-ref.html == mathvariant-5.html mathvariant-5-ref.html -fails-if(stylo) == dtls-1.html dtls-1-ref.html -fails-if(stylo) == dtls-2.html dtls-2-ref.html -fails-if(stylo) == dtls-3.html dtls-3-ref.html -fails-if(stylo) == ssty-1.html ssty-1-ref.html -fails-if(stylo) == ssty-2.html ssty-2-ref.html -fails-if(stylo) == ssty-3.html ssty-3-ref.html -fails-if(stylo) == ssty-4.html ssty-4-ref.html -fails-if(stylo) == mathscript-1.html mathscript-1-ref.html -fails-if(stylo) == mathscript-2.html mathscript-2-ref.html +== dtls-1.html dtls-1-ref.html +== dtls-2.html dtls-2-ref.html +== dtls-3.html dtls-3-ref.html +== ssty-1.html ssty-1-ref.html +== ssty-2.html ssty-2-ref.html +== ssty-3.html ssty-3-ref.html +== ssty-4.html ssty-4-ref.html +== mathscript-1.html mathscript-1-ref.html +== mathscript-2.html mathscript-2-ref.html == mo-accent-dynamic.html mo-accent-dynamic-ref.html == mo-movablelimits-dynamic.html mo-movablelimits-dynamic-ref.html == munderover-accent-dynamic.html munderover-accent-dynamic-ref.html == munderover-accentunder-dynamic.html munderover-accentunder-dynamic-ref.html == columnlines-1a.html columnlines-1-ref.html != columnlines-1b.html columnlines-1-ref.html != columnlines-1c.html columnlines-1-ref.html == columnlines-2a.html columnlines-2-ref.html
--- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -54,17 +54,17 @@ fails-if(Android) skip-if(d2d||cocoaWidg # inconsistencies. On those platforms we just test that glyph positions are # subpixel. # D2D/DirectWrite results depend on the rendering mode chosen, so considering this as random for now. skip-if(!(d2d||cocoaWidget)) random-if(d2d) != subpixel-glyphs-x-2a.html subpixel-glyphs-x-2b.html HTTP(..) == subpixel-glyphs-x-3a.html subpixel-glyphs-x-3b.html # No platforms do subpixel positioning vertically fuzzy-if(Android,19,2) == subpixel-glyphs-y-1a.html subpixel-glyphs-y-1b.html fuzzy-if(Android,231,653) == subpixel-lineheight-1a.html subpixel-lineheight-1b.html -fails-if(stylo) == swash-1.html swash-1-ref.html +== swash-1.html swash-1-ref.html HTTP(..) != synthetic-bold-metrics-01.html synthetic-bold-metrics-01-notref.html HTTP(..) == synthetic-bold-papyrus-01.html synthetic-bold-papyrus-01-ref.html # Tests for text-align-last == text-align-last-start.html text-align-last-start-ref.html == text-align-last-end.html text-align-last-end-ref.html == text-align-last-center.html text-align-last-center-ref.html == text-align-last-justify.html text-align-last-justify-ref.html == text-align-last-justify-rtl.html text-align-last-justify-rtl-ref.html @@ -150,17 +150,17 @@ HTTP(..) == zwnj-02.xhtml zwnj-02-ref.xh != zwnj-01.html zwnj-01-notref.html == initial-zwj-1.html initial-zwj-1-ref.html == cgj-01.html cgj-01-ref.html fails-if(stylo) == 444656.html 444656-ref.html fails-if(stylo) == 449555-1.html 449555-1-ref.html == 467722.html 467722-ref.html fuzzy-if(skiaContent,1,600) HTTP(..) == 475092-sub.html 475092-ref.html fails-if(Android) fuzzy-if(skiaContent&&!Android,90,3100) HTTP(..) == 475092-pos.html 475092-sub.html # bug 482596 -fails-if(stylo) == 476378-soft-hyphen-fallback.html 476378-soft-hyphen-fallback-ref.html +== 476378-soft-hyphen-fallback.html 476378-soft-hyphen-fallback-ref.html # Test for bug 484954 == rgba-text.html rgba-text-ref.html # Test for bug 575695, 'kern' table support HTTP(..) != kerning-01.html kerning-01-notref.html # Test for bug 577380, support for AAT layout (on OS X only) random-if(!cocoaWidget) == 577380.html 577380-ref.html # Test for OpenType Arabic shaping support HTTP(..) == arabic-shaping-1.html arabic-shaping-1-ref.html @@ -168,17 +168,17 @@ HTTP(..) == arabic-shaping-1.html arabic random-if(!winWidget) == arial-bold-lam-alef-1.html arial-bold-lam-alef-1-ref.html # Fallback (presentation-forms) shaping with a font that lacks GSUB/GPOS # These tests are not valid with Mac or FT2 font backends because our masking of complex-script ranges # in the 'cmap' will prevent the test font (without GSUB) being used. fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-1.html arabic-fallback-1-ref.html fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-2.html arabic-fallback-2-ref.html fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-3.html arabic-fallback-3-ref.html fails-if(!cocoaWidget&&!Android&&!stylo) HTTP(..) != arabic-fallback-4.html arabic-fallback-4-notref.html -fails-if(stylo) == arabic-marks-1.html arabic-marks-1-ref.html +== arabic-marks-1.html arabic-marks-1-ref.html == arabic-final-ligature-spacing.html arabic-final-ligature-spacing-ref.html # harfbuzz fallback mark stacking in the absence of GPOS: HTTP(..) != fallback-mark-stacking-1.html fallback-mark-stacking-1-notref.html == 726392-1.html 726392-1-ref.html == 726392-2.html 726392-2-ref.html == 726392-3.html 726392-3-ref.html == 745555-1.html 745555-1-ref.html @@ -204,39 +204,39 @@ fails-if(Android) == emoji-04.html emoji != emoji-05.html emoji-05-notref.html # check that Graphite shaping (bug 631479) is working pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) == graphite-01.html graphite-01-ref.html # Test 02 (using Pig Latin) is fuzzy on Win7 because glyph positioning is not guaranteed to match exactly # between a sequence of simple glyphs rendered individually, and the same sequence treated as a single cluster. fuzzy-if(winWidget,49,220) pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) == graphite-02.html graphite-02-ref.html pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) != graphite-03a.html graphite-03-notref.html -pref(gfx.font_rendering.graphite.enabled,true) fails-if(stylo) HTTP(..) != graphite-03b.html graphite-03-notref.html +pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) != graphite-03b.html graphite-03-notref.html pref(gfx.font_rendering.graphite.enabled,false) HTTP(..) != graphite-01.html graphite-01-ref.html pref(gfx.font_rendering.graphite.enabled,false) HTTP(..) != graphite-02.html graphite-02-ref.html # test 03a (lang setting in Padauk font) now works in opentype/harfbuzz as well pref(gfx.font_rendering.graphite.enabled,false) HTTP(..) != graphite-03a.html graphite-03-notref.html pref(gfx.font_rendering.graphite.enabled,false) HTTP(..) == graphite-03b.html graphite-03-notref.html # tests for graphite rendering with valid and invalid lang tags pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) == graphite-04-fa.html graphite-04-ref.html pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) != graphite-04-sd.html graphite-04-ref.html pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) == graphite-04-snd.html graphite-04-ref.html pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) != graphite-04-ur.html graphite-04-ref.html pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) == graphite-04-urd.html graphite-04-ref.html pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) != graphite-04-sd.html graphite-04-ur.html # tests to compare graphite to opentype (will trivially pass when graphite not enabled) -fails-if(stylo) HTTP(..) == graphite-05-ot-only.html graphite-05-ref.html -fails-if(stylo) HTTP(..) != graphite-05-ot-only.html graphite-05-fail.html +HTTP(..) == graphite-05-ot-only.html graphite-05-ref.html +HTTP(..) != graphite-05-ot-only.html graphite-05-fail.html HTTP(..) == graphite-05-simple.html graphite-05-ref.html HTTP(..) == graphite-05-multipass.html graphite-05-ref.html HTTP(..) == graphite-05-lang.html graphite-05-ref.html HTTP(..) == graphite-05-badlang.html graphite-05-ref.html -fails-if(stylo) HTTP(..) == graphite-05-feat.html graphite-05-ref.html +HTTP(..) == graphite-05-feat.html graphite-05-ref.html # comparing composed and decomposed characters that should render identically # under both OpenType and Graphite shaping pref(gfx.font_rendering.graphite.enabled,false) HTTP(..) == glyph-decomposition-opentype.html glyph-decomposition-opentype-ref.html pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) == glyph-decomposition-graphite.html glyph-decomposition-graphite-ref.html # test for bidi bug in graphite 1.3.2, fixed in 1.3.3 (bug 1207061) HTTP(..) == graphite-bidi-1.html graphite-bidi-1-ref.html @@ -299,17 +299,17 @@ HTTP(..) == graphite-surrogate-selection == auto-hyphenation-it-1.html auto-hyphenation-it-1-ref.html == auto-hyphenation-kmr-1.html auto-hyphenation-kmr-1-ref.html == auto-hyphenation-la-1.html auto-hyphenation-la-1-ref.html == auto-hyphenation-lt-1.html auto-hyphenation-lt-1-ref.html == auto-hyphenation-mn-1.html auto-hyphenation-mn-1-ref.html == auto-hyphenation-nb-1.html auto-hyphenation-nb-1-ref.html == auto-hyphenation-nl-1.html auto-hyphenation-nl-1-ref.html == auto-hyphenation-nn-1.html auto-hyphenation-nn-1-ref.html -fails-if(stylo) == auto-hyphenation-pl-1.html auto-hyphenation-pl-1-ref.html +== auto-hyphenation-pl-1.html auto-hyphenation-pl-1-ref.html == auto-hyphenation-pt-1.html auto-hyphenation-pt-1-ref.html == auto-hyphenation-ru-1.html auto-hyphenation-ru-1-ref.html == auto-hyphenation-sh-1.html auto-hyphenation-sh-1-ref.html == auto-hyphenation-sl-1.html auto-hyphenation-sl-1-ref.html == auto-hyphenation-sr-1.html auto-hyphenation-sr-1-ref.html == auto-hyphenation-sv-1.html auto-hyphenation-sv-1-ref.html # test swedish patterns != auto-hyphenation-sv-1.html auto-hyphenation-sv-1-notref.html # verify swedish != english == auto-hyphenation-tr-1.html auto-hyphenation-tr-1-ref.html
--- a/layout/style/CounterStyleManager.cpp +++ b/layout/style/CounterStyleManager.cpp @@ -1015,29 +1015,29 @@ DependentBuiltinCounterStyle::GetFallbac case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: // These styles all have a larger range than cjk-decimal, so the // only case fallback is accessed is that they are extended. // Since extending styles will cache the data themselves, we need // not cache it here. - return mManager->BuildCounterStyle(NS_LITERAL_STRING("cjk-decimal")); + return mManager->BuildCounterStyle(nsGkAtoms::cjkDecimal); default: NS_NOTREACHED("Not a valid dependent builtin style"); return BuiltinCounterStyle::GetFallback(); } } class CustomCounterStyle final : public CounterStyle { private: ~CustomCounterStyle() {} public: - CustomCounterStyle(const nsAString& aName, + CustomCounterStyle(nsIAtom* aName, CounterStyleManager* aManager, nsCSSCounterStyleRule* aRule) : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM), mName(aName), mManager(aManager), mRule(aRule), mRuleGeneration(aRule->GetGeneration()), mSystem(aRule->GetSystem()), @@ -1129,17 +1129,17 @@ private: void ComputeRawSpeakAs(uint8_t& aSpeakAs, CounterStyle*& aSpeakAsCounter); CounterStyle* ComputeSpeakAs(); CounterStyle* ComputeExtends(); CounterStyle* GetExtends(); CounterStyle* GetExtendsRoot(); - nsString mName; + nsCOMPtr<nsIAtom> mName; // CounterStyleManager should always overlive any CounterStyle as it // is owned by nsPresContext, and will be released after all nodes and // frames are released. CounterStyleManager* mManager; RefPtr<nsCSSCounterStyleRule> mRule; uint32_t mRuleGeneration; @@ -1229,17 +1229,18 @@ CustomCounterStyle::ResetDependentData() FLAG_SUFFIX_INITED | FLAG_PAD_INITED); } } /* virtual */ void CustomCounterStyle::GetStyleName(nsSubstring& aResult) { - aResult.Assign(mName); + nsDependentAtomString name(mName); + aResult.Assign(name); } /* virtual */ void CustomCounterStyle::GetPrefix(nsSubstring& aResult) { if (!(mFlags & FLAG_PREFIX_INITED)) { mFlags |= FLAG_PREFIX_INITED; @@ -1407,23 +1408,25 @@ CustomCounterStyle::GetPad(PadType& aRes aResult = mPad; } /* virtual */ CounterStyle* CustomCounterStyle::GetFallback() { if (!mFallback) { const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Fallback); - if (value.UnitHasStringValue()) { - mFallback = mManager->BuildCounterStyle( - nsDependentString(value.GetStringBufferValue())); + mFallback = CounterStyleManager::GetDecimalStyle(); + if (value.GetUnit() != eCSSUnit_Null) { + if (value.GetUnit() == eCSSUnit_AtomIdent) { + mFallback = mManager->BuildCounterStyle(value.GetAtomValue()); + } else { + MOZ_ASSERT_UNREACHABLE("Unknown unit!"); + } } else if (IsExtendsSystem()) { mFallback = GetExtends()->GetFallback(); - } else { - mFallback = CounterStyleManager::GetDecimalStyle(); } } return mFallback; } /* virtual */ uint8_t CustomCounterStyle::GetSpeakAs() { @@ -1549,20 +1552,19 @@ CustomCounterStyle::ComputeRawSpeakAs(ui const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_SpeakAs); switch (value.GetUnit()) { case eCSSUnit_Auto: aSpeakAs = GetSpeakAsAutoValue(); break; case eCSSUnit_Enumerated: aSpeakAs = value.GetIntValue(); break; - case eCSSUnit_Ident: + case eCSSUnit_AtomIdent: aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_OTHER; - aSpeakAsCounter = mManager->BuildCounterStyle( - nsDependentString(value.GetStringBufferValue())); + aSpeakAsCounter = mManager->BuildCounterStyle(value.GetAtomValue()); break; case eCSSUnit_Null: { if (!IsExtendsSystem()) { aSpeakAs = GetSpeakAsAutoValue(); } else { CounterStyle* extended = GetExtends(); if (!extended->IsCustomStyle()) { // It is safe to call GetSpeakAs on non-custom style. @@ -1659,18 +1661,17 @@ CustomCounterStyle::ComputeExtends() } if (mFlags & FLAG_EXTENDS_VISITED) { // loop detected mFlags |= FLAG_EXTENDS_LOOP; return nullptr; } const nsCSSValue& value = mRule->GetSystemArgument(); - CounterStyle* nextCounter = mManager->BuildCounterStyle( - nsDependentString(value.GetStringBufferValue())); + CounterStyle* nextCounter = mManager->BuildCounterStyle(value.GetAtomValue()); CounterStyle* target = nextCounter; if (nextCounter->IsCustomStyle()) { mFlags |= FLAG_EXTENDS_VISITED; target = static_cast<CustomCounterStyle*>(nextCounter)->ComputeExtends(); mFlags &= ~FLAG_EXTENDS_VISITED; } if (target) { @@ -1981,18 +1982,18 @@ CounterStyle::CallFallbackStyle(CounterV } static BuiltinCounterStyle gBuiltinStyleTable[NS_STYLE_LIST_STYLE__MAX]; CounterStyleManager::CounterStyleManager(nsPresContext* aPresContext) : mPresContext(aPresContext) { // Insert the static styles into cache table - mCacheTable.Put(NS_LITERAL_STRING("none"), GetNoneStyle()); - mCacheTable.Put(NS_LITERAL_STRING("decimal"), GetDecimalStyle()); + mCacheTable.Put(nsGkAtoms::none, GetNoneStyle()); + mCacheTable.Put(nsGkAtoms::decimal, GetDecimalStyle()); } CounterStyleManager::~CounterStyleManager() { MOZ_ASSERT(!mPresContext, "Disconnect should have been called"); } /* static */ void @@ -2015,17 +2016,17 @@ CounterStyleManager::Disconnect() "Counter style is still referenced by other objects."); } #endif mCacheTable.Clear(); mPresContext = nullptr; } CounterStyle* -CounterStyleManager::BuildCounterStyle(const nsSubstring& aName) +CounterStyleManager::BuildCounterStyle(nsIAtom* aName) { CounterStyle* data = mCacheTable.GetWeak(aName); if (data) { return data; } // It is intentional that the predefined names are case-insensitive // but the user-defined names case-sensitive. @@ -2035,20 +2036,22 @@ CounterStyleManager::BuildCounterStyle(c // When this assertion is removed, please remove the hack to avoid it in // nsStyleList::nsStyleList. NS_ASSERTION(styleSet->IsGecko(), "stylo: ServoStyleSets do not support custom counter " "styles yet"); nsCSSCounterStyleRule* rule = styleSet->IsGecko() ? styleSet->AsGecko()->CounterStyleRuleForName(aName) : nullptr; if (rule) { + MOZ_ASSERT(rule->Name() == aName); data = new (mPresContext) CustomCounterStyle(aName, this, rule); } else { int32_t type; - nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aName); + nsDependentAtomString name(aName); + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(name); if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable, type)) { if (gBuiltinStyleTable[type].IsDependentStyle()) { data = new (mPresContext) DependentBuiltinCounterStyle(type, this); } else { data = GetBuiltinStyle(type); } } }
--- a/layout/style/CounterStyleManager.h +++ b/layout/style/CounterStyleManager.h @@ -154,17 +154,17 @@ public: void Disconnect(); bool IsInitial() const { // only 'none' and 'decimal' return mCacheTable.Count() == 2; } - CounterStyle* BuildCounterStyle(const nsSubstring& aName); + CounterStyle* BuildCounterStyle(nsIAtom* aName); static CounterStyle* GetBuiltinStyle(int32_t aStyle); static CounterStyle* GetNoneStyle() { return GetBuiltinStyle(NS_STYLE_LIST_STYLE_NONE); } static CounterStyle* GetDecimalStyle() { @@ -178,14 +178,14 @@ public: bool NotifyRuleChanged(); nsPresContext* PresContext() const { return mPresContext; } NS_INLINE_DECL_REFCOUNTING(CounterStyleManager) private: nsPresContext* mPresContext; - nsRefPtrHashtable<nsStringHashKey, CounterStyle> mCacheTable; + nsRefPtrHashtable<nsRefPtrHashKey<nsIAtom>, CounterStyle> mCacheTable; }; } // namespace mozilla #endif /* !defined(mozilla_CounterStyleManager_h_) */
--- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -1724,16 +1724,22 @@ Gecko_CSSValue_SetString(nsCSSValueBorro void Gecko_CSSValue_SetStringFromAtom(nsCSSValueBorrowedMut aCSSValue, nsIAtom* aAtom, nsCSSUnit aUnit) { aCSSValue->SetStringValue(nsDependentAtomString(aAtom), aUnit); } void +Gecko_CSSValue_SetAtomIdent(nsCSSValueBorrowedMut aCSSValue, nsIAtom* aAtom) +{ + aCSSValue->SetAtomIdentValue(already_AddRefed<nsIAtom>(aAtom)); +} + +void Gecko_CSSValue_SetArray(nsCSSValueBorrowedMut aCSSValue, int32_t aLength) { MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null); RefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(aLength); aCSSValue->SetArrayValue(array, eCSSUnit_Array); } @@ -1788,16 +1794,27 @@ Gecko_nsStyleFont_SetLang(nsStyleFont* a void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, const nsStyleFont* aSource) { aFont->mLanguage = aSource->mLanguage; } void +Gecko_nsStyleFont_FixupNoneGeneric(nsStyleFont* aFont, + RawGeckoPresContextBorrowed aPresContext) +{ + const nsFont* defaultVariableFont = + aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID, + aFont->mLanguage); + nsRuleNode::FixupNoneGeneric(&aFont->mFont, aPresContext, + aFont->mGenericID, defaultVariableFont); +} + +void FontSizePrefs::CopyFrom(const LangGroupFontPrefs& prefs) { mDefaultVariableSize = prefs.mDefaultVariableFont.size; mDefaultFixedSize = prefs.mDefaultFixedFont.size; mDefaultSerifSize = prefs.mDefaultSerifFont.size; mDefaultSansSerifSize = prefs.mDefaultSansSerifFont.size; mDefaultMonospaceSize = prefs.mDefaultMonospaceFont.size; mDefaultCursiveSize = prefs.mDefaultCursiveFont.size;
--- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -420,25 +420,29 @@ void Gecko_CSSValue_SetNumber(nsCSSValue void Gecko_CSSValue_SetKeyword(nsCSSValueBorrowedMut css_value, nsCSSKeyword keyword); void Gecko_CSSValue_SetPercentage(nsCSSValueBorrowedMut css_value, float percent); void Gecko_CSSValue_SetCalc(nsCSSValueBorrowedMut css_value, nsStyleCoord::CalcValue calc); void Gecko_CSSValue_SetFunction(nsCSSValueBorrowedMut css_value, int32_t len); void Gecko_CSSValue_SetString(nsCSSValueBorrowedMut css_value, const uint8_t* string, uint32_t len, nsCSSUnit unit); void Gecko_CSSValue_SetStringFromAtom(nsCSSValueBorrowedMut css_value, nsIAtom* atom, nsCSSUnit unit); +// Take an addrefed nsIAtom and set it to the nsCSSValue +void Gecko_CSSValue_SetAtomIdent(nsCSSValueBorrowedMut css_value, nsIAtom* atom); void Gecko_CSSValue_SetArray(nsCSSValueBorrowedMut css_value, int32_t len); void Gecko_CSSValue_SetURL(nsCSSValueBorrowedMut css_value, ServoBundledURI uri); void Gecko_CSSValue_SetInt(nsCSSValueBorrowedMut css_value, int32_t integer, nsCSSUnit unit); void Gecko_CSSValue_Drop(nsCSSValueBorrowedMut css_value); NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList); bool Gecko_PropertyId_IsPrefEnabled(nsCSSPropertyID id); void Gecko_nsStyleFont_SetLang(nsStyleFont* font, nsIAtom* atom); void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, const nsStyleFont* aSource); +void Gecko_nsStyleFont_FixupNoneGeneric(nsStyleFont* font, + RawGeckoPresContextBorrowed pres_context); FontSizePrefs Gecko_GetBaseSize(nsIAtom* lang); struct GeckoFontMetrics { nscoord mChSize; nscoord mXSize; };
--- a/layout/style/ServoElementSnapshot.cpp +++ b/layout/style/ServoElementSnapshot.cpp @@ -27,17 +27,17 @@ ServoElementSnapshot::~ServoElementSnaps MOZ_COUNT_DTOR(ServoElementSnapshot); } void ServoElementSnapshot::AddAttrs(Element* aElement) { MOZ_ASSERT(aElement); - if (HasAny(Flags::Attributes)) { + if (HasAttrs()) { return; } uint32_t attrCount = aElement->GetAttrCount(); const nsAttrName* attrName; for (uint32_t i = 0; i < attrCount; ++i) { attrName = aElement->GetAttrNameAt(i); const nsAttrValue* attrValue =
--- a/layout/style/ServoElementSnapshot.h +++ b/layout/style/ServoElementSnapshot.h @@ -65,19 +65,19 @@ class ServoElementSnapshot typedef EventStates::ServoType ServoStateType; public: typedef ServoElementSnapshotFlags Flags; explicit ServoElementSnapshot(const Element* aElement); ~ServoElementSnapshot(); - bool HasAttrs() { return HasAny(Flags::Attributes); } + bool HasAttrs() const { return HasAny(Flags::Attributes); } - bool HasState() { return HasAny(Flags::State); } + bool HasState() const { return HasAny(Flags::State); } /** * Captures the given state (if not previously captured). */ void AddState(EventStates aState) { if (!HasAny(Flags::State)) { mState = aState.ServoValue(); @@ -90,30 +90,32 @@ public: */ void AddAttrs(Element* aElement); /** * Needed methods for attribute matching. */ BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const { + MOZ_ASSERT(HasAttrs()); if (aIndex >= mAttrs.Length()) { return BorrowedAttrInfo(nullptr, nullptr); } return BorrowedAttrInfo(&mAttrs[aIndex].mName, &mAttrs[aIndex].mValue); } const nsAttrValue* GetParsedAttr(nsIAtom* aLocalName) const { return GetParsedAttr(aLocalName, kNameSpaceID_None); } const nsAttrValue* GetParsedAttr(nsIAtom* aLocalName, int32_t aNamespaceID) const { + MOZ_ASSERT(HasAttrs()); uint32_t i, len = mAttrs.Length(); if (aNamespaceID == kNameSpaceID_None) { // This should be the common case so lets make an optimized loop for (i = 0; i < len; ++i) { if (mAttrs[i].mName.Equals(aLocalName)) { return &mAttrs[i].mValue; } } @@ -130,17 +132,17 @@ public: return nullptr; } bool IsInChromeDocument() const { return mIsInChromeDocument; } - bool HasAny(Flags aFlags) { return bool(mContains & aFlags); } + bool HasAny(Flags aFlags) const { return bool(mContains & aFlags); } private: // TODO: Profile, a 1 or 2 element AutoTArray could be worth it, given we know // we're dealing with attribute changes when we take snapshots of attributes, // though it can be wasted space if we deal with a lot of state-only // snapshots. Flags mContains; nsTArray<ServoAttrSnapshot> mAttrs;
--- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -258,19 +258,18 @@ public: nsIURI* aBaseURL, nsIPrincipal* aDocPrincipal); bool EvaluateSupportsCondition(const nsAString& aCondition, nsIURI* aDocURL, nsIURI* aBaseURL, nsIPrincipal* aDocPrincipal); - bool ParseCounterStyleName(const nsAString& aBuffer, - nsIURI* aURL, - nsAString& aName); + already_AddRefed<nsIAtom> ParseCounterStyleName(const nsAString& aBuffer, + nsIURI* aURL); bool ParseCounterDescriptor(nsCSSCounterDesc aDescID, const nsAString& aBuffer, nsIURI* aSheetURL, nsIURI* aBaseURL, nsIPrincipal* aSheetPrincipal, nsCSSValue& aValue); @@ -702,17 +701,17 @@ protected: bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet); bool ParseSupportsConditionTerms(bool& aConditionMet); enum SupportsConditionTermOperator { eAnd, eOr }; bool ParseSupportsConditionTermsAfterOperator( bool& aConditionMet, SupportsConditionTermOperator aOperator); bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData); - bool ParseCounterStyleName(nsAString& aName, bool aForDefinition); + already_AddRefed<nsIAtom> ParseCounterStyleName(bool aForDefinition); bool ParseCounterStyleNameValue(nsCSSValue& aValue); bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule); bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, nsCSSValue& aValue); bool ParseCounterRange(nsCSSValuePair& aPair); /** * Parses the current input stream for a CSS token stream value and resolves @@ -3038,31 +3037,30 @@ CSSParserImpl::ParsePropertyWithVariable // Copy the property value into the rule data. mTempData.MapRuleInfoInto(aPropertyID, aRuleData); mTempData.ClearProperty(propertyToParse); mTempData.AssertInitialState(); } -bool -CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer, - nsIURI* aURL, - nsAString& aName) +already_AddRefed<nsIAtom> +CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer, nsIURI* aURL) { nsCSSScanner scanner(aBuffer, 0); css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL); InitScanner(scanner, reporter, aURL, aURL, nullptr); - bool success = ParseCounterStyleName(aName, true) && !GetToken(true); + nsCOMPtr<nsIAtom> name = ParseCounterStyleName(true); + bool success = name && !GetToken(true); OUTPUT_ERROR(); ReleaseScanner(); - return success; + return success ? name.forget() : nullptr; } bool CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID, const nsAString& aBuffer, nsIURI* aSheetURL, nsIURI* aBaseURL, nsIPrincipal* aSheetPrincipal, @@ -4824,20 +4822,20 @@ CSSParserImpl::ParseSupportsConditionTer return true; } } } bool CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData) { - nsAutoString name; + nsCOMPtr<nsIAtom> name; uint32_t linenum, colnum; if (!GetNextTokenLocation(true, &linenum, &colnum) || - !ParseCounterStyleName(name, true)) { + !(name = ParseCounterStyleName(true))) { REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent); return false; } if (!ExpectSymbol('{', true)) { REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart); return false; } @@ -4909,55 +4907,54 @@ CSSParserImpl::ParseCounterStyleRule(Rul } if (isCorrect) { (*aAppendFunc)(rule, aData); } return true; } -bool -CSSParserImpl::ParseCounterStyleName(nsAString& aName, bool aForDefinition) +already_AddRefed<nsIAtom> +CSSParserImpl::ParseCounterStyleName(bool aForDefinition) { if (!GetToken(true)) { - return false; + return nullptr; } if (mToken.mType != eCSSToken_Ident) { UngetToken(); - return false; + return nullptr; } static const nsCSSKeyword kReservedNames[] = { eCSSKeyword_none, eCSSKeyword_decimal, eCSSKeyword_UNKNOWN }; nsCSSValue value; // we don't actually care about the value if (!ParseCustomIdent(value, mToken.mIdent, aForDefinition ? kReservedNames : nullptr)) { REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName); UngetToken(); - return false; - } - - aName = mToken.mIdent; - if (nsCSSProps::IsPredefinedCounterStyle(aName)) { - ToLowerCase(aName); - } - return true; + return nullptr; + } + + nsString name = mToken.mIdent; + if (nsCSSProps::IsPredefinedCounterStyle(name)) { + ToLowerCase(name); + } + return NS_Atomize(name); } bool CSSParserImpl::ParseCounterStyleNameValue(nsCSSValue& aValue) { - nsString name; - if (ParseCounterStyleName(name, false)) { - aValue.SetStringValue(name, eCSSUnit_Ident); + if (nsCOMPtr<nsIAtom> name = ParseCounterStyleName(false)) { + aValue.SetAtomIdentValue(name.forget()); return true; } return false; } bool CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule) { @@ -8041,17 +8038,17 @@ CSSParserImpl::ParseCounter(nsCSSValue& // get optional type int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1; nsCSSValue& type = val->Item(typeItem); if (ExpectSymbol(',', true)) { if (!ParseCounterStyleNameValue(type) && !ParseSymbols(type)) { break; } } else { - type.SetStringValue(NS_LITERAL_STRING("decimal"), eCSSUnit_Ident); + type.SetAtomIdentValue(do_AddRef(nsGkAtoms::decimal)); } if (!ExpectSymbol(')', true)) { break; } aValue.SetArrayValue(val, unit); return true; @@ -15276,19 +15273,18 @@ CSSParserImpl::ParseListStyle() } if ((found & 2) == 0) { values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, eCSSUnit_Enumerated); } if ((found & 4) == 0) { // Provide default values - nsString type = (found & 1) ? - NS_LITERAL_STRING("none") : NS_LITERAL_STRING("disc"); - values[2].SetStringValue(type, eCSSUnit_Ident); + nsIAtom* type = (found & 1) ? nsGkAtoms::none : nsGkAtoms::disc; + values[2].SetAtomIdentValue(do_AddRef(type)); } if ((found & 8) == 0) { values[3].SetNoneValue(); } // Start at 1 to avoid appending fake value. for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) { AppendValue(listStyleIDs[index], values[index]); @@ -18217,23 +18213,21 @@ nsCSSParser::ParsePropertyWithVariableRe { static_cast<CSSParserImpl*>(mImpl)-> ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID, aValue, aVariables, aRuleData, aDocURL, aBaseURL, aDocPrincipal, aSheet, aLineNumber, aLineOffset); } -bool -nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer, - nsIURI* aURL, - nsAString& aName) +already_AddRefed<nsIAtom> +nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer, nsIURI* aURL) { return static_cast<CSSParserImpl*>(mImpl)-> - ParseCounterStyleName(aBuffer, aURL, aName); + ParseCounterStyleName(aBuffer, aURL); } bool nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID, const nsAString& aBuffer, nsIURI* aSheetURL, nsIURI* aBaseURL, nsIPrincipal* aSheetPrincipal,
--- a/layout/style/nsCSSParser.h +++ b/layout/style/nsCSSParser.h @@ -299,19 +299,21 @@ public: nsRuleData* aRuleData, nsIURI* aDocURL, nsIURI* aBaseURL, nsIPrincipal* aDocPrincipal, mozilla::CSSStyleSheet* aSheet, uint32_t aLineNumber, uint32_t aLineOffset); - bool ParseCounterStyleName(const nsAString& aBuffer, - nsIURI* aURL, - nsAString& aName); + /** + * Parses a string as a counter-style name. Returns nullptr if fails. + */ + already_AddRefed<nsIAtom> ParseCounterStyleName(const nsAString& aBuffer, + nsIURI* aURL); bool ParseCounterDescriptor(nsCSSCounterDesc aDescID, const nsAString& aBuffer, nsIURI* aSheetURL, nsIURI* aBaseURL, nsIPrincipal* aSheetPrincipal, nsCSSValue& aValue);
--- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -888,17 +888,21 @@ struct RuleCascadeData { nsTArray<nsFontFaceRuleContainer> mFontFaceRules; nsTArray<nsCSSKeyframesRule*> mKeyframesRules; nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules; nsTArray<nsCSSPageRule*> mPageRules; nsTArray<nsCSSCounterStyleRule*> mCounterStyleRules; nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable; - nsDataHashtable<nsStringHashKey, nsCSSCounterStyleRule*> mCounterStyleRuleTable; + // The hashtable doesn't need to hold a strong reference to the name + // atom, because nsCSSCounterStyleRule always does. If the name changes + // we need to discard this table and rebuild it anyway. + nsDataHashtable<nsPtrHashKey<nsIAtom>, + nsCSSCounterStyleRule*> mCounterStyleRuleTable; // Looks up or creates the appropriate list in |mAttributeSelectors|. // Returns null only on allocation failure. nsTArray<SelectorPair>* AttributeListFor(nsIAtom* aAttribute); nsMediaQueryResultCacheKey mCacheKey; RuleCascadeData* mNext; // for a different medium @@ -3113,17 +3117,17 @@ nsCSSRuleProcessor::KeyframesRuleForName return cascade->mKeyframesRuleTable.Get(aName); } return nullptr; } nsCSSCounterStyleRule* nsCSSRuleProcessor::CounterStyleRuleForName(nsPresContext* aPresContext, - const nsAString& aName) + nsIAtom* aName) { RuleCascadeData* cascade = GetRuleCascade(aPresContext); if (cascade) { return cascade->mCounterStyleRuleTable.Get(aName); } return nullptr; @@ -3832,17 +3836,17 @@ nsCSSRuleProcessor::RefreshRuleCascade(n nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i]; newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule); } // Build mCounterStyleRuleTable for (nsTArray<nsCSSCounterStyleRule*>::size_type i = 0, iEnd = newCascade->mCounterStyleRules.Length(); i < iEnd; ++i) { nsCSSCounterStyleRule* rule = newCascade->mCounterStyleRules[i]; - newCascade->mCounterStyleRuleTable.Put(rule->GetName(), rule); + newCascade->mCounterStyleRuleTable.Put(rule->Name(), rule); } // mMustGatherDocumentRules controls whether we build mDocumentRules // and mDocumentCacheKey so that they can be used as keys by the // RuleProcessorCache, as obtained by TakeDocumentRulesAndCacheKey // later. We set it to false just below so that we only do this // the first time we build a RuleProcessorCache for a shared rule // processor.
--- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -205,17 +205,17 @@ public: // true for success and false for failure. bool AppendFontFaceRules(nsPresContext* aPresContext, nsTArray<nsFontFaceRuleContainer>& aArray); nsCSSKeyframesRule* KeyframesRuleForName(nsPresContext* aPresContext, const nsString& aName); nsCSSCounterStyleRule* CounterStyleRuleForName(nsPresContext* aPresContext, - const nsAString& aName); + nsIAtom* aName); bool AppendPageRules(nsPresContext* aPresContext, nsTArray<nsCSSPageRule*>& aArray); bool AppendFontFeatureValuesRules(nsPresContext* aPresContext, nsTArray<nsCSSFontFeatureValuesRule*>& aArray); /**
--- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -2420,18 +2420,19 @@ nsCSSCounterStyleRule::List(FILE* out, i { nsCString baseInd, descInd; for (int32_t indent = aIndent; --indent >= 0; ) { baseInd.AppendLiteral(" "); } descInd = baseInd; descInd.AppendLiteral(" "); + nsDependentAtomString name(mName); fprintf_stderr(out, "%s@counter-style %s (rev.%u) {\n", - baseInd.get(), NS_ConvertUTF16toUTF8(mName).get(), + baseInd.get(), NS_ConvertUTF16toUTF8(name).get(), mGeneration); // TODO fprintf_stderr(out, "%s}\n", baseInd.get()); } #endif /* virtual */ int32_t nsCSSCounterStyleRule::GetType() const @@ -2444,17 +2445,18 @@ nsCSSCounterStyleRule::Type() const { return nsIDOMCSSRule::COUNTER_STYLE_RULE; } void nsCSSCounterStyleRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AssignLiteral(u"@counter-style "); - nsStyleUtil::AppendEscapedCSSIdent(mName, aCssText); + nsDependentAtomString name(mName); + nsStyleUtil::AppendEscapedCSSIdent(name, aCssText); aCssText.AppendLiteral(u" {\n"); for (nsCSSCounterDesc id = nsCSSCounterDesc(0); id < eCSSCounterDesc_COUNT; id = nsCSSCounterDesc(id + 1)) { if (mValues[id].GetUnit() != eCSSUnit_Null) { nsAutoString tmp; // This is annoying. We want to be a const method, but kGetters stores // XPCOM method pointers, which aren't const methods. The thing is, @@ -2470,26 +2472,26 @@ nsCSSCounterStyleRule::GetCssTextImpl(ns aCssText.AppendLiteral(u"}"); } // nsIDOMCSSCounterStyleRule methods NS_IMETHODIMP nsCSSCounterStyleRule::GetName(nsAString& aName) { aName.Truncate(); - nsStyleUtil::AppendEscapedCSSIdent(mName, aName); + nsDependentAtomString name(mName); + nsStyleUtil::AppendEscapedCSSIdent(name, aName); return NS_OK; } NS_IMETHODIMP nsCSSCounterStyleRule::SetName(const nsAString& aName) { nsCSSParser parser; - nsAutoString name; - if (parser.ParseCounterStyleName(aName, nullptr, name)) { + if (nsCOMPtr<nsIAtom> name = parser.ParseCounterStyleName(aName, nullptr)) { nsIDocument* doc = GetDocument(); MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); mName = name; if (StyleSheet* sheet = GetStyleSheet()) { sheet->AsGecko()->SetModifiedByChildRule(); if (doc) { @@ -2670,23 +2672,28 @@ nsCSSCounterStyleRule::GetSpeakAs(nsAStr aSpeakAs.AssignLiteral(u"spell-out"); break; default: NS_NOTREACHED("Unknown speech synthesis"); } break; case eCSSUnit_Auto: - case eCSSUnit_Ident: + case eCSSUnit_AtomIdent: aSpeakAs.Truncate(); value.AppendToString(eCSSProperty_UNKNOWN, aSpeakAs, nsCSSValue::eNormalized); break; + case eCSSUnit_Null: + aSpeakAs.Truncate(); + break; + default: + NS_NOTREACHED("Unknown speech synthesis"); aSpeakAs.Truncate(); } return NS_OK; } nsresult nsCSSCounterStyleRule::GetDescriptor(nsCSSCounterDesc aDescID, nsAString& aValue)
--- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -481,22 +481,23 @@ protected: }; } // namespace mozilla class nsCSSCounterStyleRule final : public mozilla::css::Rule, public nsIDOMCSSCounterStyleRule { public: - explicit nsCSSCounterStyleRule(const nsAString& aName, + explicit nsCSSCounterStyleRule(nsIAtom* aName, uint32_t aLineNumber, uint32_t aColumnNumber) : mozilla::css::Rule(aLineNumber, aColumnNumber) , mName(aName) , mGeneration(0) { + MOZ_ASSERT(aName, "Must have non-null name"); } private: nsCSSCounterStyleRule(const nsCSSCounterStyleRule& aCopy); ~nsCSSCounterStyleRule(); public: NS_DECL_ISUPPORTS_INHERITED @@ -539,17 +540,17 @@ public: // The XPCOM SetFallback is OK // This function is only used to check whether a non-empty value, which has // been accepted by parser, is valid for the given system and descriptor. static bool CheckDescValue(int32_t aSystem, nsCSSCounterDesc aDescID, const nsCSSValue& aValue); - const nsString& GetName() const { return mName; } + nsIAtom* Name() const { return mName; } uint32_t GetGeneration() const { return mGeneration; } int32_t GetSystem() const; const nsCSSValue& GetSystemArgument() const; const nsCSSValue& GetDesc(nsCSSCounterDesc aDescID) const { @@ -568,14 +569,14 @@ public: private: typedef NS_STDCALL_FUNCPROTO(nsresult, Getter, nsCSSCounterStyleRule, GetSymbols, (nsAString&)); static const Getter kGetters[]; nsresult GetDescriptor(nsCSSCounterDesc aDescID, nsAString& aValue); nsresult SetDescriptor(nsCSSCounterDesc aDescID, const nsAString& aValue); - nsString mName; + nsCOMPtr<nsIAtom> mName; nsCSSValue mValues[eCSSCounterDesc_COUNT]; uint32_t mGeneration; }; #endif /* !defined(nsCSSRules_h_) */
--- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -502,16 +502,24 @@ void nsCSSValue::SetStringValue(const ns mUnit = aUnit; MOZ_ASSERT(UnitHasStringValue(), "not a string unit"); if (UnitHasStringValue()) { mValue.mString = BufferFromString(aValue).take(); } else mUnit = eCSSUnit_Null; } +void +nsCSSValue::SetAtomIdentValue(already_AddRefed<nsIAtom> aValue) +{ + Reset(); + mUnit = eCSSUnit_AtomIdent; + mValue.mAtom = aValue.take(); +} + void nsCSSValue::SetColorValue(nscolor aValue) { SetIntegerColorValue(aValue, eCSSUnit_RGBAColor); } void nsCSSValue::SetIntegerColorValue(nscolor aValue, nsCSSUnit aUnit)
--- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -900,16 +900,17 @@ public: { static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value, "aValue must be an enum that fits within mValue.mInt"); SetIntValue(static_cast<int32_t>(aValue), eCSSUnit_Enumerated); } void SetPercentValue(float aValue); void SetFloatValue(float aValue, nsCSSUnit aUnit); void SetStringValue(const nsString& aValue, nsCSSUnit aUnit); + void SetAtomIdentValue(already_AddRefed<nsIAtom> aValue); void SetColorValue(nscolor aValue); void SetIntegerColorValue(nscolor aValue, nsCSSUnit aUnit); // converts the nscoord to pixels void SetIntegerCoordValue(nscoord aCoord); void SetFloatColorValue(float aComponent1, float aComponent2, float aComponent3, float aAlpha, nsCSSUnit aUnit);
--- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -405,16 +405,48 @@ nsRuleNode::GetMetricsFor(nsPresContext* if (wm.IsVertical() && !wm.IsSideways()) { isVertical = true; } } return nsRuleNode::GetMetricsFor(aPresContext, isVertical, aStyleFont, aFontSize, aUseUserFontSet); } +/* static */ +void +nsRuleNode::FixupNoneGeneric(nsFont* aFont, + const nsPresContext* aPresContext, + uint8_t aGenericFontID, + const nsFont* aDefaultVariableFont) +{ + bool useDocumentFonts = + aPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts); + if (aGenericFontID == kGenericFont_NONE || + (!useDocumentFonts && (aGenericFontID == kGenericFont_cursive || + aGenericFontID == kGenericFont_fantasy))) { + FontFamilyType defaultGeneric = + aDefaultVariableFont->fontlist.FirstGeneric(); + MOZ_ASSERT(aDefaultVariableFont->fontlist.Length() == 1 && + (defaultGeneric == eFamily_serif || + defaultGeneric == eFamily_sans_serif)); + if (defaultGeneric != eFamily_none) { + if (useDocumentFonts) { + aFont->fontlist.SetDefaultFontType(defaultGeneric); + } else { + // Either prioritize the first generic in the list, + // or (if there isn't one) prepend the default variable font. + if (!aFont->fontlist.PrioritizeFirstGeneric()) { + aFont->fontlist.PrependGeneric(defaultGeneric); + } + } + } + } else { + aFont->fontlist.SetDefaultFontType(eFamily_none); + } +} static nsSize CalcViewportUnitsScale(nsPresContext* aPresContext) { // The caller is making use of viewport units, so notify the pres context // that it will need to rebuild the rule tree if the size of the viewport // changes. aPresContext->SetUsesViewportUnits(true); @@ -3551,40 +3583,19 @@ nsRuleNode::SetFont(nsPresContext* aPres // font-family: font family list, enum, inherit const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); NS_ASSERTION(eCSSUnit_Enumerated != familyValue->GetUnit(), "system fonts should not be in mFamily anymore"); if (eCSSUnit_FontFamilyList == familyValue->GetUnit()) { // set the correct font if we are using DocumentFonts OR we are overriding for XUL // MJA: bug 31816 - bool useDocumentFonts = - aPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts); - if (aGenericFontID == kGenericFont_NONE || - (!useDocumentFonts && (aGenericFontID == kGenericFont_cursive || - aGenericFontID == kGenericFont_fantasy))) { - FontFamilyType defaultGeneric = - defaultVariableFont->fontlist.FirstGeneric(); - MOZ_ASSERT(defaultVariableFont->fontlist.Length() == 1 && - (defaultGeneric == eFamily_serif || - defaultGeneric == eFamily_sans_serif)); - if (defaultGeneric != eFamily_none) { - if (useDocumentFonts) { - aFont->mFont.fontlist.SetDefaultFontType(defaultGeneric); - } else { - // Either prioritize the first generic in the list, - // or (if there isn't one) prepend the default variable font. - if (!aFont->mFont.fontlist.PrioritizeFirstGeneric()) { - aFont->mFont.fontlist.PrependGeneric(defaultGeneric); - } - } - } - } else { - aFont->mFont.fontlist.SetDefaultFontType(eFamily_none); - } + nsRuleNode::FixupNoneGeneric(&aFont->mFont, aPresContext, + aGenericFontID, defaultVariableFont); + aFont->mFont.systemFont = false; // Technically this is redundant with the code below, but it's good // to have since we'll still want it once we get rid of // SetGenericFont (bug 380915). aFont->mGenericID = aGenericFontID; } else if (eCSSUnit_System_Font == familyValue->GetUnit()) { aFont->mFont.fontlist = systemFont.fontlist; @@ -7946,51 +7957,49 @@ nsRuleNode::ComputeListData(void* aStart switch (typeValue->GetUnit()) { case eCSSUnit_Unset: case eCSSUnit_Inherit: { conditions.SetUncacheable(); list->SetCounterStyle(parentList->GetCounterStyle()); break; } case eCSSUnit_Initial: - list->SetListStyleType(NS_LITERAL_STRING("disc"), mPresContext); - break; - case eCSSUnit_Ident: { - nsString typeIdent; - typeValue->GetStringValue(typeIdent); - list->SetListStyleType(typeIdent, mPresContext); + list->SetListStyleType(nsGkAtoms::disc, mPresContext); + break; + case eCSSUnit_AtomIdent: { + list->SetListStyleType(typeValue->GetAtomValue(), mPresContext); break; } case eCSSUnit_String: { nsString str; typeValue->GetStringValue(str); list->SetCounterStyle(new AnonymousCounterStyle(str)); break; } case eCSSUnit_Enumerated: { // For compatibility with html attribute map. // This branch should never be called for value from CSS. int32_t intValue = typeValue->GetIntValue(); - nsAutoString name; + nsCOMPtr<nsIAtom> name; switch (intValue) { case NS_STYLE_LIST_STYLE_LOWER_ROMAN: - name.AssignLiteral(u"lower-roman"); + name = nsGkAtoms::lowerRoman; break; case NS_STYLE_LIST_STYLE_UPPER_ROMAN: - name.AssignLiteral(u"upper-roman"); + name = nsGkAtoms::upperRoman; break; case NS_STYLE_LIST_STYLE_LOWER_ALPHA: - name.AssignLiteral(u"lower-alpha"); + name = nsGkAtoms::lowerAlpha; break; case NS_STYLE_LIST_STYLE_UPPER_ALPHA: - name.AssignLiteral(u"upper-alpha"); + name = nsGkAtoms::upperAlpha; break; default: - CopyASCIItoUTF16(nsCSSProps::ValueToKeyword( - intValue, nsCSSProps::kListStyleKTable), name); + name = NS_Atomize(nsCSSProps::ValueToKeyword( + intValue, nsCSSProps::kListStyleKTable)); break; } list->SetListStyleType(name, mPresContext); break; } case eCSSUnit_Symbols: list->SetCounterStyle(new AnonymousCounterStyle(typeValue->GetArrayValue())); break;
--- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -806,16 +806,25 @@ public: bool aUseUserFontSet); static already_AddRefed<nsFontMetrics> GetMetricsFor(nsPresContext* aPresContext, nsStyleContext* aStyleContext, const nsStyleFont* aStyleFont, nscoord aFontSize, bool aUseUserFontSet); + /** + * Appropriately add the correct font if we are using DocumentFonts or + * overriding for XUL + */ + static void FixupNoneGeneric(nsFont* aFont, + const nsPresContext* aPresContext, + uint8_t aGenericFontID, + const nsFont* aDefaultVariableFont); + // Transition never returns null; on out of memory it'll just return |this|. nsRuleNode* Transition(nsIStyleRule* aRule, mozilla::SheetType aLevel, bool aIsImportantRule); nsRuleNode* GetParent() const { return mParent; } bool IsRoot() const { return mParent == nullptr; } // Return the root of the rule tree that this rule node is in. nsRuleNode* RuleTree();
--- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -2250,17 +2250,17 @@ nsStyleSet::KeyframesRuleForName(const n ruleProc->KeyframesRuleForName(presContext, aName); if (result) return result; } return nullptr; } nsCSSCounterStyleRule* -nsStyleSet::CounterStyleRuleForName(const nsAString& aName) +nsStyleSet::CounterStyleRuleForName(nsIAtom* aName) { NS_ENSURE_FALSE(mInShutdown, nullptr); NS_ASSERTION(mBatching == 0, "rule processors out of date"); nsPresContext* presContext = PresContext(); for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) { if (gCSSSheetTypes[i] == SheetType::ScopedDoc) continue;
--- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -303,17 +303,17 @@ class nsStyleSet final // Append all the currently-active font face rules to aArray. Return // true for success and false for failure. bool AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray); // Return the winning (in the cascade) @keyframes rule for the given name. nsCSSKeyframesRule* KeyframesRuleForName(const nsString& aName); // Return the winning (in the cascade) @counter-style rule for the given name. - nsCSSCounterStyleRule* CounterStyleRuleForName(const nsAString& aName); + nsCSSCounterStyleRule* CounterStyleRuleForName(nsIAtom* aName); // Fetch object for looking up font feature values already_AddRefed<gfxFontFeatureValueSet> GetFontFeatureValuesLookup(); // Append all the currently-active font feature values rules to aArray. // Return true for success and false for failure. bool AppendFontFeatureValuesRules( nsTArray<nsCSSFontFeatureValuesRule*>& aArray);
--- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -600,18 +600,18 @@ nsStyleList::nsStyleList(const nsPresCon : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE) { MOZ_COUNT_CTOR(nsStyleList); if (aContext->StyleSet()->IsServo()) { // FIXME: bug 1328319. mCounterStyle = CounterStyleManager::GetBuiltinStyle(NS_STYLE_LIST_STYLE_DISC); } else { - mCounterStyle = aContext->CounterStyleManager()-> - BuildCounterStyle(NS_LITERAL_STRING("disc")); + mCounterStyle = aContext-> + CounterStyleManager()->BuildCounterStyle(nsGkAtoms::disc); } SetQuotesInitial(); } nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); }
--- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1477,18 +1477,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt } void SetCounterStyle(mozilla::CounterStyle* aStyle) { // NB: This function is called off-main-thread during parallel restyle, but // only with builtin styles that use dummy refcounting. MOZ_ASSERT(NS_IsMainThread() || !aStyle->IsDependentStyle()); mCounterStyle = aStyle; } - void SetListStyleType(const nsSubstring& aType, - nsPresContext* aPresContext) + void SetListStyleType(nsIAtom* aType, nsPresContext* aPresContext) { SetCounterStyle(aPresContext->CounterStyleManager()->BuildCounterStyle(aType)); } const nsStyleQuoteValues::QuotePairArray& GetQuotePairs() const; void SetQuotesInherit(const nsStyleList* aOther); void SetQuotesInitial();
--- a/layout/style/test/stylo-failures.md +++ b/layout/style/test/stylo-failures.md @@ -129,17 +129,17 @@ to mochitest command. * font-variant-{alternates,east-asian,ligatures,numeric} properties servo/servo#15957 * test_property_syntax_errors.html `font-variant-alternates` [2] * test_value_storage.html `font-variant` [176] * test_specified_value_serialization.html `bug-721136` [1] * Properties implemented but not in geckolib: * font-feature-settings property servo/servo#15975 * test_inherit_storage.html `font-feature-settings` [2] * test_initial_storage.html `font-feature-settings` [1] - * test_value_storage.html `font-feature-settings` [118] + * test_value_storage.html `font-feature-settings` [40] * image-orientation property bug 1341758 * test_value_storage.html `image-orientation` [40] * Stylesheet cloning is somehow busted bug 1348481 * test_selectors.html `matched clone` [3] * Unsupported prefixed values * moz-prefixed gradient functions bug 1337655 * test_value_storage.html `-moz-linear-gradient` [322] * ... `-moz-radial-gradient` [309] @@ -154,18 +154,16 @@ to mochitest command. * test_flexbox_flex_shorthand.html `-moz-fit-content` [4] * test_value_storage.html `-moz-max-content` [46] * ... `-moz-min-content` [6] * ... `-moz-fit-content` [6] * ... `-moz-available` [4] * -webkit-{flex,inline-flex} for display servo/servo#15400 * test_webkit_flex_display.html [4] * Unsupported values - * SVG-only values of pointer-events not recognized - * test_value_storage.html `pointer-events` [1] * new syntax of rgba?() and hsla?() functions servo/rust-cssparser#113 * test_computed_style.html `css-color-4` [2] * SVG-in-OpenType values not supported servo/servo#15211 bug 1355412 * test_value_storage.html `context-` [7] * test_bug798843_pref.html [7] * Incorrect parsing * Incorrect bounds * test_bug664955.html `font size is larger than max font size` [2] @@ -202,17 +200,16 @@ to mochitest command. * test_value_storage.html `'font'` [128] * test_shorthand_property_getters.html `font shorthand` [1] * test_system_font_serialization.html [5] * clamp negative value from calc() servo/servo#15296 * test_value_storage.html `font-size: calc(` [3] * ... `font-size: var(--a)` [3] * Negative value should be rejected * test_property_syntax_errors.html `transition-duration`: servo/servo#15343 [20] - * ... `'text-shadow'`: third length of text-shadow servo/servo#15999 [2] * Quirks mode support * hashless color servo/servo#15341 * test_property_syntax_errors.html `color: 000000` [22] * ... `color: 96ed2a` [22] * ... `color: fff` [4] * unitless length servo/servo#15342 * test_property_syntax_errors.html ` 20 ` [6] * ... `: 10 ` [6]
new file mode 100644 --- /dev/null +++ b/media/libaom/README_MOZILLA @@ -0,0 +1,21 @@ +This directory contains build files for the aom video +codec reference implementation. The actual library +source is in $TOPSRCDIR/third_party/aom/ + +Any patches or additional configuration to be applied to the +upstream source should be kept here in the media/libaom +directory. + +To update the library source and build config files, execute + + ./mach vendor aom + +To update to a specific upstream git tag or commit, use + + ./mach vendor aom -r <commit> + +The upstream aom git repository is: + + https://aomedia.googlesource.com/aom + +The git commit ID used was 4d668d7feb1f8abd809d1bca0418570a7f142a36 (Wed May 03 17:10:13 2017 +0000).
new file mode 100644 --- /dev/null +++ b/media/libaom/config/aom_version.h @@ -0,0 +1,7 @@ +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define VERSION_PATCH 0 +#define VERSION_EXTRA "" +#define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH)) +#define VERSION_STRING_NOSP "Release" +#define VERSION_STRING " Release"
new file mode 100644 --- /dev/null +++ b/media/libaom/config/generic/aom_config.asm @@ -0,0 +1,155 @@ +@ This file was created from a .asm file +@ using the ads2gas.pl script. + .equ DO1STROUNDING, 0 +.equ ARCH_ARM , 0 +.equ ARCH_MIPS , 0 +.equ ARCH_X86 , 0 +.equ ARCH_X86_64 , 0 +.equ HAVE_EDSP , 0 +.equ HAVE_MEDIA , 0 +.equ HAVE_NEON , 0 +.equ HAVE_NEON_ASM , 0 +.equ HAVE_MIPS32 , 0 +.equ HAVE_DSPR2 , 0 +.equ HAVE_MSA , 0 +.equ HAVE_MIPS64 , 0 +.equ HAVE_MMX , 0 +.equ HAVE_SSE , 0 +.equ HAVE_SSE2 , 0 +.equ HAVE_SSE3 , 0 +.equ HAVE_SSSE3 , 0 +.equ HAVE_SSE4_1 , 0 +.equ HAVE_AVX , 0 +.equ HAVE_AVX2 , 0 +.equ HAVE_AOM_PORTS , 1 +.equ HAVE_FEXCEPT , 1 +.equ HAVE_PTHREAD_H , 1 +.equ HAVE_WXWIDGETS , 0 +.equ CONFIG_DEPENDENCY_TRACKING , 1 +.equ CONFIG_EXTERNAL_BUILD , 1 +.equ CONFIG_INSTALL_DOCS , 1 +.equ CONFIG_INSTALL_BINS , 1 +.equ CONFIG_INSTALL_LIBS , 1 +.equ CONFIG_INSTALL_SRCS , 0 +.equ CONFIG_DEBUG , 0 +.equ CONFIG_GPROF , 0 +.equ CONFIG_GCOV , 0 +.equ CONFIG_RVCT , 0 +.equ CONFIG_GCC , 1 +.equ CONFIG_MSVS , 0 +.equ CONFIG_PIC , 1 +.equ CONFIG_BIG_ENDIAN , 0 +.equ CONFIG_CODEC_SRCS , 0 +.equ CONFIG_DEBUG_LIBS , 0 +.equ CONFIG_RUNTIME_CPU_DETECT , 0 +.equ CONFIG_POSTPROC , 0 +.equ CONFIG_MULTITHREAD , 1 +.equ CONFIG_INTERNAL_STATS , 0 +.equ CONFIG_AV1_ENCODER , 1 +.equ CONFIG_AV1_DECODER , 1 +.equ CONFIG_AV1 , 1 +.equ CONFIG_ENCODERS , 1 +.equ CONFIG_DECODERS , 1 +.equ CONFIG_STATIC_MSVCRT , 0 +.equ CONFIG_SPATIAL_RESAMPLING , 1 +.equ CONFIG_REALTIME_ONLY , 0 +.equ CONFIG_ONTHEFLY_BITPACKING , 0 +.equ CONFIG_ERROR_CONCEALMENT , 0 +.equ CONFIG_SHARED , 0 +.equ CONFIG_STATIC , 1 +.equ CONFIG_SMALL , 0 +.equ CONFIG_POSTPROC_VISUALIZER , 0 +.equ CONFIG_OS_SUPPORT , 1 +.equ CONFIG_UNIT_TESTS , 0 +.equ CONFIG_WEBM_IO , 1 +.equ CONFIG_LIBYUV , 1 +.equ CONFIG_ACCOUNTING , 0 +.equ CONFIG_INSPECTION , 0 +.equ CONFIG_DECODE_PERF_TESTS , 0 +.equ CONFIG_ENCODE_PERF_TESTS , 0 +.equ CONFIG_COEFFICIENT_RANGE_CHECKING , 0 +.equ CONFIG_LOWBITDEPTH , 1 +.equ CONFIG_HIGHBITDEPTH , 0 +.equ CONFIG_EXPERIMENTAL , 0 +.equ CONFIG_SIZE_LIMIT , 1 +.equ CONFIG_FP_MB_STATS , 0 +.equ CONFIG_CDEF , 1 +.equ CONFIG_VAR_TX , 0 +.equ CONFIG_RECT_TX , 1 +.equ CONFIG_REF_MV , 1 +.equ CONFIG_TPL_MV , 0 +.equ CONFIG_DUAL_FILTER , 1 +.equ CONFIG_CONVOLVE_ROUND , 0 +.equ CONFIG_COMPOUND_ROUND , 0 +.equ CONFIG_EXT_TX , 0 +.equ CONFIG_TX64X64 , 0 +.equ CONFIG_SUB8X8_MC , 0 +.equ CONFIG_EXT_INTRA , 1 +.equ CONFIG_INTRA_INTERP , 0 +.equ CONFIG_FILTER_INTRA , 0 +.equ CONFIG_INTRABC , 0 +.equ CONFIG_EXT_INTER , 0