author | Ryan VanderMeulen <ryanvm@gmail.com> |
Thu, 01 Sep 2016 12:15:35 -0400 | |
changeset 353582 | 292a3b62983fd33329bca5a27534db2299dcbc4c |
parent 353581 | 28b4b2e50fb01c2a4ac6f9edf74b13c86c064b00 (current diff) |
parent 353548 | 4dd14de3fa420bf5d750ca7c969ace2a9a9931ac (diff) |
child 353583 | 32a569f4df061afba57235815ee860dae64166d3 |
push id | 6570 |
push user | raliiev@mozilla.com |
push date | Mon, 14 Nov 2016 12:26:13 +0000 |
treeherder | mozilla-beta@f455459b2ae5 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 51.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
|
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -59,20 +59,18 @@ if (isGonk) { return libcutils; }); } XPCOMUtils.defineLazyServiceGetter(Services, 'captivePortalDetector', '@mozilla.org/toolkit/captive-detector;1', 'nsICaptivePortalDetector'); -if (AppConstants.MOZ_SAFE_BROWSING) { - XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", - "resource://gre/modules/SafeBrowsing.jsm"); -} +XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", + "resource://gre/modules/SafeBrowsing.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "SafeMode", "resource://gre/modules/SafeMode.jsm"); window.performance.measure('gecko-shell-jsm-loaded', 'gecko-shell-loadstart'); function debug(str) { dump(' -*- Shell.js: ' + str + '\n'); @@ -429,21 +427,19 @@ var shell = { window.performance.mark('gecko-shell-system-frame-set'); ppmm.addMessageListener("content-handler", this); ppmm.addMessageListener("dial-handler", this); ppmm.addMessageListener("sms-handler", this); ppmm.addMessageListener("mail-handler", this); ppmm.addMessageListener("file-picker", this); - if (AppConstants.MOZ_SAFE_BROWSING) { - setTimeout(function() { - SafeBrowsing.init(); - }, 5000); - } + setTimeout(function() { + SafeBrowsing.init(); + }, 5000); }, stop: function shell_stop() { window.removeEventListener('unload', this); window.removeEventListener('keydown', this, true); window.removeEventListener('keyup', this, true); window.removeEventListener('MozApplicationManifest', this); window.removeEventListener('sizemodechange', this);
--- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -12,18 +12,16 @@ MOZ_UA_OS_AGNOSTIC=1 MOZ_B2G_VERSION=2.6.0.0-prerelease MOZ_B2G_OS_NAME=Boot2Gecko MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official # MOZ_APP_DISPLAYNAME is set by branding/configure.sh -MOZ_SAFE_BROWSING=1 - MOZ_NO_SMART_CARDS=1 MOZ_APP_STATIC_INI=1 MOZ_NO_EV_CERTS=1 if test "$OS_TARGET" = "Android"; then MOZ_CAPTURE=1 MOZ_RAW=1 MOZ_AUDIO_CHANNEL_MANAGER=1
--- a/b2g/graphene/confvars.sh +++ b/b2g/graphene/confvars.sh @@ -19,17 +19,16 @@ MOZ_APP_UA_NAME=Firefox MOZ_B2G_VERSION=2.6.0.0-prerelease MOZ_B2G_OS_NAME=Boot2Gecko MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official # MOZ_APP_DISPLAYNAME is set by branding/configure.sh -MOZ_SAFE_BROWSING=1 MOZ_CAPTIVEDETECT=1 MOZ_NO_SMART_CARDS=1 MOZ_APP_STATIC_INI=1 NSS_NO_LIBPKIX=1 if test "$OS_TARGET" = "Android"; then MOZ_CAPTURE=1
--- a/browser/base/content/browser-doctype.inc +++ b/browser/base/content/browser-doctype.inc @@ -8,18 +8,16 @@ <!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetMenu.dtd" > %charsetDTD; <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" > %textcontextDTD; <!ENTITY % customizeToolbarDTD SYSTEM "chrome://global/locale/customizeToolbar.dtd"> %customizeToolbarDTD; <!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd"> %placesDTD; -#ifdef MOZ_SAFE_BROWSING <!ENTITY % safebrowsingDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd"> %safebrowsingDTD; -#endif <!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd"> %aboutHomeDTD; <!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> %syncBrandDTD; ]>
--- a/browser/base/content/browser-safebrowsing.js +++ b/browser/base/content/browser-safebrowsing.js @@ -1,15 +1,12 @@ /* 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/. */ -// Note: this file is not shipped (through jar.mn) -// if MOZ_SAFE_BROWSING is not defined. - var gSafeBrowsing = { setReportPhishingMenu: function() { // In order to detect whether or not we're at the phishing warning // page, we have to check the documentURI instead of the currentURI. // This is because when the DocShell loads an error page, the // currentURI stays at the original target, while the documentURI // will point to the internal error page we loaded instead.
--- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -758,17 +758,17 @@ html|*#fullscreen-exit-button { /* notification anchors should only be visible when their associated notifications are */ .notification-anchor-icon { -moz-user-focus: normal; } -.blocked-permission-icon:not([showing]), +#blocked-permissions-container > .blocked-permission-icon:not([showing]), .notification-anchor-icon:not([showing]) { display: none; } #invalid-form-popup > description { max-width: 280px; }
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -52,20 +52,18 @@ Cu.import("resource://gre/modules/Notifi ["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"], ["Weave", "resource://services-sync/main.js"], ["fxAccounts", "resource://gre/modules/FxAccounts.jsm"], ["gDevTools", "resource://devtools/client/framework/gDevTools.jsm"], ["gDevToolsBrowser", "resource://devtools/client/framework/gDevTools.jsm"], ["webrtcUI", "resource:///modules/webrtcUI.jsm", ] ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource)); -if (AppConstants.MOZ_SAFE_BROWSING) { - XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", - "resource://gre/modules/SafeBrowsing.jsm"); -} +XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", + "resource://gre/modules/SafeBrowsing.jsm"); if (AppConstants.MOZ_CRASHREPORTER) { XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter", "resource:///modules/ContentCrashHandlers.jsm"); } // lazy service getters [ @@ -1141,20 +1139,18 @@ var gBrowserInit = { } // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3. // Such callers expect that window.arguments[0] is handled as a single URI. else { loadOneOrMoreURIs(uriToLoad); } } - if (AppConstants.MOZ_SAFE_BROWSING) { - // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008. - setTimeout(function() { SafeBrowsing.init(); }, 2000); - } + // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008. + setTimeout(function() { SafeBrowsing.init(); }, 2000); Services.obs.addObserver(gIdentityHandler, "perm-changed", false); Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false); @@ -7372,17 +7368,17 @@ var gIdentityHandler = { _createPermissionItem: function (aPermission) { let container = document.createElement("hbox"); container.setAttribute("class", "identity-popup-permission-item"); container.setAttribute("align", "center"); let img = document.createElement("image"); let classes = "identity-popup-permission-icon " + aPermission.id + "-icon"; if (aPermission.state == SitePermissions.BLOCK) - classes += " blocked"; + classes += " blocked-permission-icon"; if (aPermission.inUse) classes += " in-use"; img.setAttribute("class", classes); let nameLabel = document.createElement("label"); nameLabel.setAttribute("flex", "1"); nameLabel.setAttribute("class", "identity-popup-permission-label"); nameLabel.textContent = SitePermissions.getPermissionLabel(aPermission.id);
--- a/browser/base/content/global-scripts.inc +++ b/browser/base/content/global-scripts.inc @@ -18,19 +18,17 @@ <script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/> <script type="application/javascript" src="chrome://browser/content/browser-fullScreenAndPointerLock.js"/> <script type="application/javascript" src="chrome://browser/content/browser-fullZoom.js"/> <script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.js"/> <script type="application/javascript" src="chrome://browser/content/browser-media.js"/> <script type="application/javascript" src="chrome://browser/content/browser-places.js"/> <script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/> <script type="application/javascript" src="chrome://browser/content/browser-refreshblocker.js"/> -#ifdef MOZ_SAFE_BROWSING <script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/> -#endif <script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/> <script type="application/javascript" src="chrome://browser/content/browser-social.js"/> <script type="application/javascript" src="chrome://browser/content/browser-syncui.js"/> <script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/> <script type="application/javascript" src="chrome://browser/content/browser-thumbnails.js"/> <script type="application/javascript" src="chrome://browser/content/browser-trackingprotection.js"/> #ifdef MOZ_DATA_REPORTING
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -690,18 +690,21 @@ nsContextMenu.prototype = { if (this.target instanceof Ci.nsIImageLoadingContent && this.target.currentURI) { this.onImage = true; var request = this.target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST); if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE)) this.onLoadedImage = true; - if (request && (request.imageStatus & request.STATUS_LOAD_COMPLETE)) + if (request && + (request.imageStatus & request.STATUS_LOAD_COMPLETE) && + !(request.imageStatus & request.STATUS_ERROR)) { this.onCompletedImage = true; + } this.mediaURL = this.target.currentURI.spec; var descURL = this.target.getAttribute("longdesc"); if (descURL) { this.imageDescURL = makeURLAbsolute(ownerDoc.body.baseURI, descURL); } }
--- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -737,18 +737,19 @@ function openTourPage() let scope = {} Components.utils.import("resource:///modules/UITour.jsm", scope); openUILinkIn(scope.UITour.url, "tab"); } function buildHelpMenu() { // Enable/disable the "Report Web Forgery" menu item. - if (typeof gSafeBrowsing != "undefined" && AppConstants.MOZ_SAFE_BROWSING) + if (typeof gSafeBrowsing != "undefined") { gSafeBrowsing.setReportPhishingMenu(); + } } function isElementVisible(aElement) { if (!aElement) return false; // If aElement or a direct or indirect parent is hidden or collapsed,
--- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -82,19 +82,17 @@ browser.jar: content/browser/browser-fullScreenAndPointerLock.js (content/browser-fullScreenAndPointerLock.js) content/browser/browser-fullZoom.js (content/browser-fullZoom.js) content/browser/browser-fxaccounts.js (content/browser-fxaccounts.js) content/browser/browser-gestureSupport.js (content/browser-gestureSupport.js) content/browser/browser-media.js (content/browser-media.js) content/browser/browser-places.js (content/browser-places.js) content/browser/browser-plugins.js (content/browser-plugins.js) content/browser/browser-refreshblocker.js (content/browser-refreshblocker.js) -#ifdef MOZ_SAFE_BROWSING content/browser/browser-safebrowsing.js (content/browser-safebrowsing.js) -#endif content/browser/browser-sidebar.js (content/browser-sidebar.js) content/browser/browser-social.js (content/browser-social.js) content/browser/browser-syncui.js (content/browser-syncui.js) * content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml) #ifdef CAN_DRAW_IN_TITLEBAR content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar.js) #else content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar-stub.js) @@ -186,15 +184,13 @@ browser.jar: content/browser/webrtcIndicator.js (content/webrtcIndicator.js) #endif #ifdef XP_WIN content/browser/win6BrowserOverlay.xul (content/win6BrowserOverlay.xul) #endif # the following files are browser-specific overrides * content/browser/license.html (/toolkit/content/license.html) % override chrome://global/content/license.html chrome://browser/content/license.html -#ifdef MOZ_SAFE_BROWSING content/browser/report-phishing-overlay.xul (content/report-phishing-overlay.xul) content/browser/blockedSite.xhtml (content/blockedSite.xhtml) % overlay chrome://browser/content/browser.xul chrome://browser/content/report-phishing-overlay.xul -#endif % override chrome://global/content/netError.xhtml chrome://browser/content/aboutNetError.xhtml
--- a/browser/components/about/AboutRedirector.cpp +++ b/browser/components/about/AboutRedirector.cpp @@ -30,23 +30,21 @@ struct RedirEntry { Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome privileges. This is potentially dangerous. Please use URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below unless your about: page really needs chrome privileges. Security review is required before adding new map entries without URI_SAFE_FOR_UNTRUSTED_CONTENT. */ static RedirEntry kRedirMap[] = { -#ifdef MOZ_SAFE_BROWSING { "blocked", "chrome://browser/content/blockedSite.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT }, -#endif { "certerror", "chrome://browser/content/aboutNetError.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT }, { "socialerror", "chrome://browser/content/aboutSocialError.xhtml", nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT },
--- a/browser/components/build/nsModule.cpp +++ b/browser/components/build/nsModule.cpp @@ -80,19 +80,17 @@ static const mozilla::Module::CIDEntry k static const mozilla::Module::ContractIDEntry kBrowserContracts[] = { { NS_BROWSERDIRECTORYPROVIDER_CONTRACTID, &kNS_BROWSERDIRECTORYPROVIDER_CID }, #if defined(XP_WIN) { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID }, #elif defined(MOZ_WIDGET_GTK) { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID }, #endif { NS_FEEDSNIFFER_CONTRACTID, &kNS_FEEDSNIFFER_CID }, -#ifdef MOZ_SAFE_BROWSING { NS_ABOUT_MODULE_CONTRACTID_PREFIX "blocked", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, -#endif { NS_ABOUT_MODULE_CONTRACTID_PREFIX "certerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "socialerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "providerdirectory", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "tabcrashed", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
--- a/browser/components/moz.build +++ b/browser/components/moz.build @@ -45,23 +45,21 @@ EXTRA_COMPONENTS += [ 'nsBrowserGlue.js', ] EXTRA_JS_MODULES += [ 'distribution.js', ] BROWSER_CHROME_MANIFESTS += [ + 'safebrowsing/content/test/browser.ini', 'tests/browser/browser.ini' ] XPCSHELL_TESTS_MANIFESTS += [ 'tests/unit/xpcshell.ini' ] -if CONFIG['MOZ_SAFE_BROWSING']: - BROWSER_CHROME_MANIFESTS += ['safebrowsing/content/test/browser.ini'] - with Files('safebrowsing/*'): BUG_COMPONENT = ('Toolkit', 'Phishing Protection') with Files('controlcenter/**'): BUG_COMPONENT = ('Firefox', 'General')
--- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -27,17 +27,16 @@ if test "$OS_ARCH" = "WINNT"; then fi fi fi fi # Enable building ./signmar and running libmar signature tests MOZ_ENABLE_SIGNMAR=1 -MOZ_SAFE_BROWSING=1 MOZ_APP_VERSION=$FIREFOX_VERSION MOZ_APP_VERSION_DISPLAY=$FIREFOX_VERSION_DISPLAY MOZ_EXTENSIONS_DEFAULT=" gio" # MOZ_APP_DISPLAYNAME will be set by branding/configure.sh # MOZ_BRANDING_DIRECTORY is the default branding directory used when none is # specified. It should never point to the "official" branding directory. # For mozilla-beta, mozilla-release, or mozilla-central repositories, use # "unofficial" branding.
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -591,23 +591,21 @@ @RESPATH@/components/extensions-toolkit.manifest @RESPATH@/browser/components/extensions-browser.manifest ; Modules @RESPATH@/browser/modules/* @RESPATH@/modules/* ; Safe Browsing -#ifdef MOZ_URL_CLASSIFIER @RESPATH@/components/nsURLClassifier.manifest @RESPATH@/components/nsUrlClassifierHashCompleter.js @RESPATH@/components/nsUrlClassifierListManager.js @RESPATH@/components/nsUrlClassifierLib.js @RESPATH@/components/url-classifier.xpt -#endif ; Private Browsing @RESPATH@/components/privatebrowsing.xpt @RESPATH@/components/PrivateBrowsing.manifest @RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js ; Security Reports @RESPATH@/components/SecurityReporter.manifest
--- a/browser/locales/jar.mn +++ b/browser/locales/jar.mn @@ -48,20 +48,18 @@ locale/browser/downloads/downloads.dtd (%chrome/browser/downloads/downloads.dtd) locale/browser/downloads/downloads.properties (%chrome/browser/downloads/downloads.properties) locale/browser/places/places.dtd (%chrome/browser/places/places.dtd) locale/browser/places/places.properties (%chrome/browser/places/places.properties) locale/browser/places/editBookmarkOverlay.dtd (%chrome/browser/places/editBookmarkOverlay.dtd) locale/browser/places/bookmarkProperties.properties (%chrome/browser/places/bookmarkProperties.properties) locale/browser/preferences/selectBookmark.dtd (%chrome/browser/preferences/selectBookmark.dtd) locale/browser/places/moveBookmarks.dtd (%chrome/browser/places/moveBookmarks.dtd) -#ifdef MOZ_SAFE_BROWSING locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd) locale/browser/safebrowsing/report-phishing.dtd (%chrome/browser/safebrowsing/report-phishing.dtd) -#endif locale/browser/feeds/subscribe.dtd (%chrome/browser/feeds/subscribe.dtd) locale/browser/feeds/subscribe.properties (%chrome/browser/feeds/subscribe.properties) locale/browser/migration/migration.dtd (%chrome/browser/migration/migration.dtd) locale/browser/migration/migration.properties (%chrome/browser/migration/migration.properties) locale/browser/preferences/advanced.dtd (%chrome/browser/preferences/advanced.dtd) locale/browser/preferences/applicationManager.dtd (%chrome/browser/preferences/applicationManager.dtd) locale/browser/preferences/applicationManager.properties (%chrome/browser/preferences/applicationManager.properties) locale/browser/preferences/blocklists.dtd (%chrome/browser/preferences/blocklists.dtd)
--- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -216,18 +216,18 @@ if test -n "$MOZ_INSTALL_TRACKING"; then AC_SUBST(MOZ_INSTALL_TRACKING) MOZ_ANDROID_AAR(play-services-ads, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) fi ]) dnl Configure an Android SDK. -dnl Arg 1: target SDK version, like 22. -dnl Arg 2: build tools version, like 22.0.1. +dnl Arg 1: target SDK version, like 23. +dnl Arg 2: list of build-tools versions, like "23.0.3 23.0.1". AC_DEFUN([MOZ_ANDROID_SDK], [ MOZ_ARG_WITH_STRING(android-sdk, [ --with-android-sdk=DIR location where the Android SDK can be found (like ~/.mozbuild/android-sdk-linux)], android_sdk_root=$withval) @@ -249,22 +249,31 @@ case "$target" in android_target_sdk=$1 AC_MSG_CHECKING([for Android SDK platform version $android_target_sdk]) android_sdk=$android_sdk_root/platforms/android-$android_target_sdk if ! test -e "$android_sdk/source.properties" ; then AC_MSG_ERROR([You must download Android SDK platform version $android_target_sdk. Try |mach bootstrap|. (Looked for $android_sdk)]) fi AC_MSG_RESULT([$android_sdk]) - android_build_tools="$android_sdk_root"/build-tools/$2 - AC_MSG_CHECKING([for Android build-tools version $2]) - if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then - AC_MSG_RESULT([$android_build_tools]) - else - AC_MSG_ERROR([You must install the Android build-tools version $2. Try |mach bootstrap|. (Looked for $android_build_tools)]) + AC_MSG_CHECKING([for Android build-tools]) + android_build_tools_base="$android_sdk_root"/build-tools + android_build_tools_version="" + versions=($2) + for version in $versions; do + android_build_tools="$android_build_tools_base"/$version + if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then + android_build_tools_version=$version + AC_MSG_RESULT([$android_build_tools]) + break + fi + done + if test "$android_build_tools_version" == ""; then + version=$(echo $versions | cut -d" " -f1) + AC_MSG_ERROR([You must install the Android build-tools version $version. Try |mach bootstrap|. (Looked for "$android_build_tools_base"/$version)]) fi MOZ_PATH_PROG(ZIPALIGN, zipalign, :, [$android_build_tools]) MOZ_PATH_PROG(DX, dx, :, [$android_build_tools]) MOZ_PATH_PROG(AAPT, aapt, :, [$android_build_tools]) MOZ_PATH_PROG(AIDL, aidl, :, [$android_build_tools]) if test -z "$ZIPALIGN" -o "$ZIPALIGN" = ":"; then AC_MSG_ERROR([The program zipalign was not found. Try |mach bootstrap|.]) @@ -304,17 +313,17 @@ case "$target" in if test -z "$EMULATOR" -o "$EMULATOR" = ":"; then AC_MSG_ERROR([The program emulator was not found. Try |mach bootstrap|.]) fi ANDROID_TARGET_SDK="${android_target_sdk}" ANDROID_SDK="${android_sdk}" ANDROID_SDK_ROOT="${android_sdk_root}" ANDROID_TOOLS="${android_tools}" - ANDROID_BUILD_TOOLS_VERSION="$2" + ANDROID_BUILD_TOOLS_VERSION="$android_build_tools_version" AC_DEFINE_UNQUOTED(ANDROID_TARGET_SDK,$ANDROID_TARGET_SDK) AC_SUBST(ANDROID_TARGET_SDK) AC_SUBST(ANDROID_SDK_ROOT) AC_SUBST(ANDROID_SDK) AC_SUBST(ANDROID_TOOLS) AC_SUBST(ANDROID_BUILD_TOOLS_VERSION) MOZ_ANDROID_AAR(customtabs, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
--- a/devtools/client/debugger/moz.build +++ b/devtools/client/debugger/moz.build @@ -8,9 +8,12 @@ DIRS += [ 'new' ] DevToolsModules( 'debugger-commands.js', 'panel.js' ) -BROWSER_CHROME_MANIFESTS += ['test/mochitest/browser.ini'] +BROWSER_CHROME_MANIFESTS += [ + 'new/test/mochitest/browser.ini', + 'test/mochitest/browser.ini' +]
new file mode 100644 --- /dev/null +++ b/devtools/client/debugger/new/test/mochitest/browser.ini @@ -0,0 +1,5 @@ +[DEFAULT] +tags = devtools +subsuite = devtools + +[browser_dbg_stub.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_stub.js @@ -0,0 +1,7 @@ + +add_task(function*() { + ok(true, + "This is a stub so that we can run the new debugger tests " + + "by copying them in here. This will go away once we land " + + "the initial suite of new tests"); +});
--- a/devtools/client/definitions.js +++ b/devtools/client/definitions.js @@ -177,24 +177,38 @@ Tools.jsdebugger = { return true; }, build: function (iframeWindow, toolbox) { return new DebuggerPanel(iframeWindow, toolbox); } }; -if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) { - const NewDebuggerPanel = require("devtools/client/debugger/new/panel").DebuggerPanel; +function switchDebugger() { + if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) { + const NewDebuggerPanel = require("devtools/client/debugger/new/panel").DebuggerPanel; - Tools.jsdebugger.url = "chrome://devtools/content/debugger/new/index.html"; - Tools.jsdebugger.build = function (iframeWindow, toolbox) { - return new NewDebuggerPanel(iframeWindow, toolbox); - }; + Tools.jsdebugger.url = "chrome://devtools/content/debugger/new/index.html"; + Tools.jsdebugger.build = function (iframeWindow, toolbox) { + return new NewDebuggerPanel(iframeWindow, toolbox); + }; + } else { + Tools.jsdebugger.url = "chrome://devtools/content/debugger/debugger.xul"; + Tools.jsdebugger.build = function (iframeWindow, toolbox) { + return new DebuggerPanel(iframeWindow, toolbox); + }; + } } +switchDebugger(); + +Services.prefs.addObserver( + "devtools.debugger.new-debugger-frontend", + { observe: switchDebugger }, + false +); Tools.styleEditor = { id: "styleeditor", key: l10n("open.commandkey", styleEditorStrings), ordinal: 4, visibilityswitch: "devtools.styleeditor.enabled", accesskey: l10n("open.accesskey", styleEditorStrings), modifiers: "shift",
--- a/devtools/client/shared/components/frame.js +++ b/devtools/client/shared/components/frame.js @@ -171,17 +171,18 @@ module.exports = createClass({ let functionDisplayName = frame.functionDisplayName; if (!functionDisplayName && showAnonymousFunctionName) { functionDisplayName = webl10n.getStr("stacktrace.anonymousFunction"); } if (functionDisplayName) { elements.push( dom.span({ className: "frame-link-function-display-name" }, - functionDisplayName) + functionDisplayName), + " " ); } } let displaySource = showFullSourceUrl ? long : short; if (isSourceMapped) { displaySource = getSourceMappedFile(displaySource); } else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) { @@ -231,14 +232,14 @@ module.exports = createClass({ } else { sourceEl = dom.span({ className: "frame-link-source", }, sourceInnerEl); } elements.push(sourceEl); if (showHost && host) { - elements.push(dom.span({ className: "frame-link-host" }, host)); + elements.push(" ", dom.span({ className: "frame-link-host" }, host)); } return dom.span(attributes, ...elements); } });
--- a/devtools/client/shared/components/stack-trace.js +++ b/devtools/client/shared/components/stack-trace.js @@ -37,32 +37,32 @@ const StackTrace = createClass({ }, render() { let { stacktrace, onViewSourceInDebugger } = this.props; let frames = []; stacktrace.forEach(s => { if (s.asyncCause) { - frames.push(AsyncFrame({ + frames.push("\t", AsyncFrame({ asyncCause: s.asyncCause - })); + }), "\n"); } - frames.push(Frame({ + frames.push("\t", Frame({ frame: { functionDisplayName: s.functionName, source: s.filename.split(" -> ").pop(), line: s.lineNumber, column: s.columnNumber, }, showFunctionName: true, showAnonymousFunctionName: true, showFullSourceUrl: true, onClick: onViewSourceInDebugger - })); + }), "\n"); }); return dom.div({ className: "stack-trace" }, frames); } }); module.exports = StackTrace;
--- a/devtools/client/shared/components/test/mochitest/test_stack-trace.html +++ b/devtools/client/shared/components/test/mochitest/test_stack-trace.html @@ -40,17 +40,19 @@ window.onload = function() { }; let trace = ReactDOM.render(StackTrace(props), window.document.body); yield forceRender(trace); let traceEl = trace.getDOMNode(); ok(traceEl, "Rendered StackTrace has an element"); - let frameEls = traceEl.childNodes; + // Get the child nodes and filter out the text-only whitespace ones + let frameEls = Array.from(traceEl.childNodes) + .filter(n => n.className.includes("frame")); ok(frameEls, "Rendered StackTrace has frames"); is(frameEls.length, 3, "StackTrace has 3 frames"); // Check the top frame, function name should be anonymous checkFrameString({ el: frameEls[0], functionName: "<anonymous>", source: "http://myfile.com/mahscripts.js", @@ -71,13 +73,21 @@ window.onload = function() { functionName: "loadFunc", source: "http://myfile.com/loadee.js", file: "http://myfile.com/loadee.js", line: 10, column: null, shouldLink: true, tooltip: "View source in Debugger → http://myfile.com/loadee.js:10", }); + + // Check the tabs and newlines in the stack trace textContent + let traceText = traceEl.textContent; + let traceLines = traceText.split("\n"); + ok(traceLines.length > 0, "There are newlines in the stack trace text"); + is(traceLines.pop(), "", "There is a newline at the end of the stack trace text"); + is(traceLines.length, 3, "The stack trace text has 3 lines"); + ok(traceLines.every(l => l[0] == "\t"), "Every stack trace line starts with tab"); }); } </script> </body> </html>
--- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -930,18 +930,16 @@ Messages.Simple.prototype = extend(Messa twisty.addEventListener("click", this._onClickCollapsible); this.element.appendChild(twisty); this.collapsible = true; this.element.setAttribute("collapsible", true); } this.element.appendChild(body); - this.element.appendChild(this.document.createTextNode("\n")); - this.element.clipboardText = this.element.textContent; if (this.private) { this.element.setAttribute("private", true); } // TODO: handle object releasing in a more elegant way once all console // messages use the new API - bug 778766. @@ -988,22 +986,26 @@ Messages.Simple.prototype = extend(Messa // do this before repeatNode is rendered - it has no effect afterwards this._repeatID.textContent += "|" + container.textContent; let repeatNode = this._renderRepeatNode(); let location = this._renderLocation(); if (repeatNode) { + bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(repeatNode); } if (location) { + bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(location); } + bodyFlex.appendChild(this.document.createTextNode("\n")); + if (this.stack) { this._attachment = new Widgets.Stacktrace(this, this.stack).render().element; } if (this._attachment) { bodyWrapper.appendChild(this._attachment); }
--- a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js +++ b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js @@ -1,69 +1,97 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* globals goDoCommand */ + "use strict"; // Test copying of the entire console message when right-clicked // with no other text selected. See Bug 1100562. -function test() { +add_task(function* () { let hud; let outputNode; let contextMenu; - const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + - "test/test-console.html"; - - Task.spawn(runner).then(finishTest); + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console.html"; - function* runner() { - const {tab} = yield loadTab(TEST_URI); - hud = yield openConsole(tab); - outputNode = hud.outputNode; - contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); + const { tab, browser } = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + outputNode = hud.outputNode; + contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); + + registerCleanupFunction(() => { + hud = outputNode = contextMenu = null; + }); - registerCleanupFunction(() => { - hud = outputNode = contextMenu = null; - }); + hud.jsterm.clearOutput(); - hud.jsterm.clearOutput(); - content.console.log("bug 1100562"); + yield ContentTask.spawn(browser, {}, function* () { + let button = content.document.getElementById("testTrace"); + button.click(); + }); - let [results] = yield waitForMessages({ - webconsole: hud, - messages: [{ + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { text: "bug 1100562", category: CATEGORY_WEBDEV, severity: SEVERITY_LOG, - }] - }); + lines: 1, + }, + { + name: "console.trace output", + consoleTrace: true, + lines: 3, + }, + ] + }); - outputNode.focus(); - let message = [...results.matched][0]; + outputNode.focus(); - yield waitForContextMenu(contextMenu, message, copyFromPopup, - testContextMenuCopy); + for (let result of results) { + let message = [...result.matched][0]; - function copyFromPopup() { + yield waitForContextMenu(contextMenu, message, () => { let copyItem = contextMenu.querySelector("#cMenu_copy"); copyItem.doCommand(); let controller = top.document.commandDispatcher .getControllerForCommand("cmd_copy"); is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); - } + }); + + let clipboardText; + + yield waitForClipboardPromise( + () => goDoCommand("cmd_copy"), + (str) => { + clipboardText = str; + return message.textContent == clipboardText; + } + ); + + ok(clipboardText, "Clipboard text was found and saved"); - function testContextMenuCopy() { - waitForClipboard((str) => { - return message.textContent.trim() == str.trim(); - }, () => { - goDoCommand("cmd_copy"); - }, () => {}, () => {} - ); + let lines = clipboardText.split("\n"); + ok(lines.length > 0, "There is at least one newline in the message"); + is(lines.pop(), "", "There is a newline at the end"); + is(lines.length, result.lines, `There are ${result.lines} lines in the message`); + + // Test the first line for "timestamp message repeat file:line" + let firstLine = lines.shift(); + ok(/^[\d:.]+ .+ \d+ .+:\d+$/.test(firstLine), + "The message's first line has the right format"); + + // Test the remaining lines (stack trace) for "TABfunctionName sourceURL:line:col" + for (let line of lines) { + ok(/^\t.+ .+:\d+:\d+$/.test(line), "The stack trace line has the right format"); } + } - yield closeConsole(tab); - } -} + yield closeConsole(tab); + yield finishTest(); +});
--- a/devtools/client/webconsole/test/test-console.html +++ b/devtools/client/webconsole/test/test-console.html @@ -8,20 +8,27 @@ }; function test() { var str = "Dolske Digs Bacon, Now and Forevermore." for (var i=0; i < 5; i++) { console.log(str); } } + + function testTrace() { + console.log("bug 1100562"); + console.trace(); + } + console.info("INLINE SCRIPT:"); test(); console.warn("I'm warning you, he will eat up all yr bacon."); console.error("Error Message"); </script> </head> <body> <h1 id="header">Heads Up Display Demo</h1> <button onclick="test();">Log stuff about Dolske</button> + <button id="testTrace" onclick="testTrace();">Log stuff with stacktrace</button> <div id="myDiv"></div> </body> </html>
--- a/devtools/shared/l10n.js +++ b/devtools/shared/l10n.js @@ -1,44 +1,55 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const parsePropertiesFile = require("devtools/shared/node-properties/node-properties"); const { sprintf } = require("devtools/shared/sprintfjs/sprintf"); +const propertiesMap = {}; + +/** + * Memoized getter for properties files that ensures a given url is only required and + * parsed once. + * + * @param {String} url + * The URL of the properties file to parse. + * @return {Object} parsed properties mapped in an object. + */ +function getProperties(url) { + if (!propertiesMap[url]) { + propertiesMap[url] = parsePropertiesFile(require(`raw!${url}`)); + } + + return propertiesMap[url]; +} + /** * Localization convenience methods. * * @param string stringBundleName * The desired string bundle's name. */ function LocalizationHelper(stringBundleName) { this.stringBundleName = stringBundleName; } LocalizationHelper.prototype = { - get properties() { - if (!this._properties) { - this._properties = parsePropertiesFile(require(`raw!${this.stringBundleName}`)); - } - - return this._properties; - }, - /** * L10N shortcut function. * * @param string name * @return string */ getStr: function (name) { - if (name in this.properties) { - return this.properties[name]; + let properties = getProperties(this.stringBundleName); + if (name in properties) { + return properties[name]; } throw new Error("No localization found for [" + name + "]"); }, /** * L10N shortcut function. * @@ -98,16 +109,73 @@ LocalizationHelper.prototype = { return number.toLocaleString(undefined, { maximumFractionDigits: decimals, minimumFractionDigits: decimals }); } }; +function getPropertiesForNode(node) { + let bundleEl = node.closest("[data-localization-bundle]"); + if (!bundleEl) { + return null; + } + + let propertiesUrl = bundleEl.getAttribute("data-localization-bundle"); + return getProperties(propertiesUrl); +} + +/** + * Translate existing markup annotated with data-localization attributes. + * + * How to use data-localization in markup: + * + * <div data-localization="content=myContent;title=myTitle"/> + * + * The data-localization attribute identifies an element as being localizable. + * The content of the attribute is semi-colon separated list of descriptors. + * - "title=myTitle" means the "title" attribute should be replaced with the localized + * string corresponding to the key "myTitle". + * - "content=myContent" means the text content of the node should be replaced by the + * string corresponding to "myContent" + * + * How to define the localization bundle in markup: + * + * <div data-localization-bundle="url/to/my.properties"> + * [...] + * <div data-localization="content=myContent;title=myTitle"/> + * + * Set the data-localization-bundle on an ancestor of the nodes that should be localized. + * + * @param {Element} root + * The root node to use for the localization + */ +function localizeMarkup(root) { + let elements = root.querySelectorAll("[data-localization]"); + for (let element of elements) { + let properties = getPropertiesForNode(element); + if (!properties) { + continue; + } + + let attributes = element.getAttribute("data-localization").split(";"); + for (let attribute of attributes) { + let [name, value] = attribute.trim().split("="); + if (name === "content") { + element.textContent = properties[value]; + } else { + element.setAttribute(name, properties[value]); + } + } + + element.removeAttribute("data-localization"); + } +} + const sharedL10N = new LocalizationHelper("devtools-shared/locale/shared.properties"); const ELLIPSIS = sharedL10N.getStr("ellipsis"); /** * A helper for having the same interface as LocalizationHelper, but for more * than one file. Useful for abstracting l10n string locations. */ function MultiLocalizationHelper(...stringBundleNames) { @@ -135,10 +203,11 @@ function MultiLocalizationHelper(...stri } } return null; }; }); } exports.LocalizationHelper = LocalizationHelper; +exports.localizeMarkup = localizeMarkup; exports.MultiLocalizationHelper = MultiLocalizationHelper; exports.ELLIPSIS = ELLIPSIS;
--- a/devtools/shared/tests/browser/browser.ini +++ b/devtools/shared/tests/browser/browser.ini @@ -1,7 +1,8 @@ [DEFAULT] tags = devtools subsuite = devtools support-files = ../../../server/tests/browser/head.js [browser_async_storage.js] +[browser_l10n_localizeMarkup.js]
new file mode 100644 --- /dev/null +++ b/devtools/shared/tests/browser/browser_l10n_localizeMarkup.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the markup localization works properly. + +const { localizeMarkup, LocalizationHelper } = require("devtools/shared/l10n"); + +add_task(function* () { + info("Check that the strings used for this test are still valid"); + let INSPECTOR_L10N = new LocalizationHelper("devtools/locale/inspector.properties"); + let TOOLBOX_L10N = new LocalizationHelper("devtools/locale/toolbox.properties"); + let str1 = INSPECTOR_L10N.getStr("inspector.label"); + let str2 = INSPECTOR_L10N.getStr("inspector.commandkey"); + let str3 = TOOLBOX_L10N.getStr("toolbox.defaultTitle"); + ok(str1 && str2 && str3, "If this failed, strings should be updated in the test"); + + info("Create the test markup"); + let div = document.createElement("div"); + div.innerHTML = + `<div data-localization-bundle="devtools/locale/inspector.properties"> + <div id="d0" data-localization="content=inspector.someInvalidKey"></div> + <div id="d1" data-localization="content=inspector.label">Text will disappear</div> + <div id="d2" data-localization="content=inspector.label;title=inspector.commandkey"> + </div> + <!-- keep the following data-localization on two separate lines --> + <div id="d3" data-localization="content=inspector.label; + title=inspector.commandkey"></div> + <div id="d4" data-localization="aria-label=inspector.label">Some content</div> + <div data-localization-bundle="devtools/locale/toolbox.properties"> + <div id="d5" data-localization="content=toolbox.defaultTitle"></div> + </div> + </div> + `; + + info("Use localization helper to localize the test markup"); + localizeMarkup(div); + + let div1 = div.querySelector("#d1"); + let div2 = div.querySelector("#d2"); + let div3 = div.querySelector("#d3"); + let div4 = div.querySelector("#d4"); + let div5 = div.querySelector("#d5"); + + is(div1.innerHTML, str1, "The content of #d1 is localized"); + is(div2.innerHTML, str1, "The content of #d2 is localized"); + is(div2.getAttribute("title"), str2, "The title of #d2 is localized"); + is(div3.innerHTML, str1, "The content of #d3 is localized"); + is(div3.getAttribute("title"), str2, "The title of #d3 is localized"); + is(div4.innerHTML, "Some content", "The content of #d4 is not replaced"); + is(div4.getAttribute("aria-label"), str1, "The aria-label of #d4 is localized"); + is(div5.innerHTML, str3, "The content of #d5 is localized with another bundle"); +});
--- a/dom/animation/AnimationEffectReadOnly.cpp +++ b/dom/animation/AnimationEffectReadOnly.cpp @@ -138,39 +138,45 @@ AnimationEffectReadOnly::GetComputedTimi } const TimeDuration& localTime = aLocalTime.Value(); // Calculate the time within the active interval. // https://w3c.github.io/web-animations/#active-time StickyTimeDuration activeTime; StickyTimeDuration beforeActiveBoundary = - std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime); + std::max(std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime), + zeroDuration); + StickyTimeDuration activeAfterBoundary = - std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration), - result.mEndTime); + std::max(std::min(StickyTimeDuration(aTiming.mDelay + + result.mActiveDuration), + result.mEndTime), + zeroDuration); if (localTime > activeAfterBoundary || (aPlaybackRate >= 0 && localTime == activeAfterBoundary)) { result.mPhase = ComputedTiming::AnimationPhase::After; if (!result.FillsForwards()) { // The animation isn't active or filling at this time. return result; } - activeTime = std::max(std::min(result.mActiveDuration, - result.mActiveDuration + aTiming.mEndDelay), - zeroDuration); + activeTime = + std::max(std::min(StickyTimeDuration(localTime - aTiming.mDelay), + result.mActiveDuration), + zeroDuration); } else if (localTime < beforeActiveBoundary || (aPlaybackRate < 0 && localTime == beforeActiveBoundary)) { result.mPhase = ComputedTiming::AnimationPhase::Before; if (!result.FillsBackwards()) { // The animation isn't active or filling at this time. return result; } - // activeTime is zero + activeTime = std::max(StickyTimeDuration(localTime - aTiming.mDelay), + zeroDuration); } else { MOZ_ASSERT(result.mActiveDuration != zeroDuration, "How can we be in the middle of a zero-duration interval?"); result.mPhase = ComputedTiming::AnimationPhase::Active; activeTime = localTime - aTiming.mDelay; } // Convert active time to a multiple of iterations.
--- a/dom/animation/TimingParams.h +++ b/dom/animation/TimingParams.h @@ -109,17 +109,18 @@ struct TimingParams return zeroDuration; } return mDuration->MultDouble(mIterations); } StickyTimeDuration EndTime() const { - return mDelay + ActiveDuration() + mEndDelay; + return std::max(mDelay + ActiveDuration() + mEndDelay, + StickyTimeDuration()); } bool operator==(const TimingParams& aOther) const; bool operator!=(const TimingParams& aOther) const { return !(*this == aOther); } };
--- a/dom/animation/test/css-animations/file_animation-computed-timing.html +++ b/dom/animation/test/css-animations/file_animation-computed-timing.html @@ -187,17 +187,17 @@ test(function(t) { 'Initial value of endTime'); }, 'endTime of an infinitely repeating zero-duration animation'); test(function(t) { // Fill forwards so div.getAnimations()[0] won't return an // undefined value. var div = addDiv(t, {style: 'animation: moveAnimation 10s -100s forwards'}); var effect = div.getAnimations()[0].effect; - assert_equals(effect.getComputedTiming().endTime, -90 * MS_PER_SEC, + assert_equals(effect.getComputedTiming().endTime, 0, 'Initial value of endTime'); }, 'endTime of an animation that finishes before its startTime'); // -------------------- // activeDuration // = iteration duration * iteration count // --------------------
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -274,61 +274,76 @@ public: NS_LITERAL_STRING("error"), false, false); } }; /** * This listener observes the first video frame to arrive with a non-empty size, - * and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size. + * and calls HTMLMediaElement::UpdateInitialMediaSize() with that size. */ class HTMLMediaElement::StreamSizeListener : public DirectMediaStreamTrackListener { public: explicit StreamSizeListener(HTMLMediaElement* aElement) : mElement(aElement), mInitialSizeFound(false) {} + void Forget() { mElement = nullptr; } void ReceivedSize(gfx::IntSize aSize) { + MOZ_ASSERT(NS_IsMainThread()); + if (!mElement) { return; } + RefPtr<HTMLMediaElement> deathGrip = mElement; mElement->UpdateInitialMediaSize(aSize); } void NotifyRealtimeTrackData(MediaStreamGraph* aGraph, StreamTime aTrackOffset, const MediaSegment& aMedia) override { - if (mInitialSizeFound || aMedia.GetType() != MediaSegment::VIDEO) { + if (mInitialSizeFound) { return; } + + if (aMedia.GetType() != MediaSegment::VIDEO) { + MOZ_ASSERT(false, "Should only lock on to a video track"); + return; + } + const VideoSegment& video = static_cast<const VideoSegment&>(aMedia); for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) { if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) { mInitialSizeFound = true; nsCOMPtr<nsIRunnable> event = - NewRunnableMethod<gfx::IntSize>( - this, &StreamSizeListener::ReceivedSize, - c->mFrame.GetIntrinsicSize()); - aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); + NewRunnableMethod<gfx::IntSize>(this, &StreamSizeListener::ReceivedSize, + c->mFrame.GetIntrinsicSize()); + // This is fine to dispatch straight to main thread (instead of via + // ...AfterStreamUpdate()) since it reflects state of the element, + // not the stream. Events reflecting stream or track state should be + // dispatched so their order is preserved. + NS_DispatchToMainThread(event.forget()); return; } } } private: // These fields may only be accessed on the main thread HTMLMediaElement* mElement; - // These fields may only be accessed on the MSG thread + // These fields may only be accessed on the MSG's appending thread. + // (this is a direct listener so we get called by whoever is producing + // this track's data) bool mInitialSizeFound; }; /** * There is a reference cycle involving this class: MediaLoadListener * holds a reference to the HTMLMediaElement, which holds a reference * to an nsIChannel, which holds a reference to this listener. * We break the reference cycle in OnStartRequest by clearing mElement. @@ -2570,26 +2585,31 @@ HTMLMediaElement::CaptureStreamInternal( } // mAudioCaptured tells the user that the audio played by this media element // is being routed to the captureStreams *instead* of being played to // speakers. mAudioCaptured = true; } + if (mDecoder) { + out->mCapturingDecoder = true; + mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(), + aFinishWhenEnded); + } else if (mSrcStream) { + out->mCapturingMediaStream = true; + } + if (mReadyState == HAVE_NOTHING) { - // Do not expose the tracks directly before we have metadata. + // Do not expose the tracks until we have metadata. RefPtr<DOMMediaStream> result = out->mStream; return result.forget(); } if (mDecoder) { - out->mCapturingDecoder = true; - mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(), - aFinishWhenEnded); if (HasAudio()) { TrackID audioTrackId = mMediaInfo.mAudio.mTrackId; RefPtr<MediaStreamTrackSource> trackSource = getter->GetMediaStreamTrackSource(audioTrackId); RefPtr<MediaStreamTrack> track = out->mStream->CreateDOMTrack(audioTrackId, MediaSegment::AUDIO, trackSource); out->mStream->AddTrackInternal(track); @@ -2605,32 +2625,16 @@ HTMLMediaElement::CaptureStreamInternal( trackSource); out->mStream->AddTrackInternal(track); LOG(LogLevel::Debug, ("Created video track %d for captured decoder", videoTrackId)); } } if (mSrcStream) { - out->mCapturingMediaStream = true; - MediaStream* inputStream = out->mStream->GetInputStream(); - if (!inputStream) { - NS_ERROR("No input stream"); - RefPtr<DOMMediaStream> result = out->mStream; - return result.forget(); - } - - ProcessedMediaStream* processedInputStream = - inputStream->AsProcessedStream(); - if (!processedInputStream) { - NS_ERROR("Input stream not a ProcessedMediaStream"); - RefPtr<DOMMediaStream> result = out->mStream; - return result.forget(); - } - for (size_t i = 0; i < AudioTracks()->Length(); ++i) { AudioTrack* t = (*AudioTracks())[i]; if (t->Enabled()) { AddCaptureMediaTrackToOutputStream(t, *out, false); } } if (IsVideo() && !out->mCapturingAudioOnly) { // Only add video tracks if we're a video element and the output stream
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2549,17 +2549,17 @@ TabParent::GetAuthPrompt(uint32_t aPromp // of the dialogs works as it should when using tabs. nsCOMPtr<nsISupports> prompt; rv = wwatch->GetPrompt(window, iid, getter_AddRefs(prompt)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsILoginManagerPrompter> prompter = do_QueryInterface(prompt); if (prompter) { nsCOMPtr<nsIDOMElement> browser = do_QueryInterface(mFrameElement); - prompter->SetE10sData(browser, nullptr); + prompter->SetBrowser(browser); } *aResult = prompt.forget().take(); return NS_OK; } PColorPickerParent* TabParent::AllocPColorPickerParent(const nsString& aTitle,
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -237,17 +237,16 @@ MediaDecoderStateMachine::MediaDecoderSt mIsVideoPrerolling(false), mAudioCaptured(false), INIT_WATCHABLE(mAudioCompleted, false), INIT_WATCHABLE(mVideoCompleted, false), mNotifyMetadataBeforeFirstFrame(false), mQuickBuffering(false), mMinimizePreroll(false), mDecodeThreadWaiting(false), - mDecodingFirstFrame(true), mSentLoadedMetadataEvent(false), mSentFirstFrameLoadedEvent(false), mSentPlaybackEndedEvent(false), mVideoDecodeSuspended(false), mVideoDecodeSuspendTimer(mTaskQueue), mOutputStreamManager(new OutputStreamManager()), mResource(aDecoder->GetResource()), mAudioOffloading(false), @@ -483,25 +482,26 @@ bool MediaDecoderStateMachine::HaveEnoug bool MediaDecoderStateMachine::NeedToDecodeVideo() { MOZ_ASSERT(OnTaskQueue()); SAMPLE_LOG("NeedToDecodeVideo() isDec=%d minPrl=%d enufVid=%d", IsVideoDecoding(), mMinimizePreroll, HaveEnoughDecodedVideo()); return IsVideoDecoding() && mState != DECODER_STATE_SEEKING && - ((IsDecodingFirstFrame() && VideoQueue().GetSize() == 0) || + ((!mSentFirstFrameLoadedEvent && VideoQueue().GetSize() == 0) || (!mMinimizePreroll && !HaveEnoughDecodedVideo())); } bool MediaDecoderStateMachine::NeedToSkipToNextKeyframe() { MOZ_ASSERT(OnTaskQueue()); - if (IsDecodingFirstFrame()) { + // Don't skip when we're still decoding first frames. + if (!mSentFirstFrameLoadedEvent) { return false; } MOZ_ASSERT(mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING || mState == DECODER_STATE_SEEKING); // Since GetClock() can only be called after starting MediaSink, we return // false quickly if it is not started because we won't fall behind playback @@ -552,17 +552,17 @@ bool MediaDecoderStateMachine::NeedToDecodeAudio() { MOZ_ASSERT(OnTaskQueue()); SAMPLE_LOG("NeedToDecodeAudio() isDec=%d minPrl=%d enufAud=%d", IsAudioDecoding(), mMinimizePreroll, HaveEnoughDecodedAudio()); return IsAudioDecoding() && mState != DECODER_STATE_SEEKING && - ((IsDecodingFirstFrame() && AudioQueue().GetSize() == 0) || + ((!mSentFirstFrameLoadedEvent && AudioQueue().GetSize() == 0) || (!mMinimizePreroll && !HaveEnoughDecodedAudio())); } void MediaDecoderStateMachine::OnAudioDecoded(MediaData* aAudioSample) { MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(mState != DECODER_STATE_SEEKING); @@ -728,17 +728,17 @@ MediaDecoderStateMachine::OnNotDecoded(M } } } bool MediaDecoderStateMachine::MaybeFinishDecodeFirstFrame() { MOZ_ASSERT(OnTaskQueue()); - if (!IsDecodingFirstFrame() || + if (mSentFirstFrameLoadedEvent || (IsAudioDecoding() && AudioQueue().GetSize() == 0) || (IsVideoDecoding() && VideoQueue().GetSize() == 0)) { return false; } FinishDecodeFirstFrame(); if (!mQueuedSeek.Exists()) { return false; } @@ -785,17 +785,17 @@ MediaDecoderStateMachine::OnVideoDecoded // arrive, increase the amount of audio we buffer to ensure that we // don't run out of audio. This is unnecessary for async readers, // since they decode audio and video on different threads so they // are unlikely to run out of decoded audio. if (mReader->IsAsync()) { return; } TimeDuration decodeTime = TimeStamp::Now() - aDecodeStartTime; - if (!IsDecodingFirstFrame() && + if (mSentFirstFrameLoadedEvent && THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs && !HasLowUndecodedData()) { mLowAudioThresholdUsecs = std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), mAmpleAudioThresholdUsecs); mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs); DECODER_LOG("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld", @@ -1072,17 +1072,16 @@ MediaDecoderStateMachine::ExitState(Stat } void MediaDecoderStateMachine::EnterState(State aState) { MOZ_ASSERT(OnTaskQueue()); switch (aState) { case DECODER_STATE_DECODING_METADATA: - mDecodingFirstFrame = true; ReadMetadata(); break; case DECODER_STATE_DORMANT: DiscardSeekTaskIfExist(); if (IsPlaying()) { StopPlayback(); } Reset(); @@ -1255,29 +1254,21 @@ MediaDecoderStateMachine::Shutdown() } void MediaDecoderStateMachine::StartDecoding() { MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(mState == DECODER_STATE_DECODING); - if (mDecodingFirstFrame && mSentFirstFrameLoadedEvent) { - // We're resuming from dormant state, so we don't need to request - // the first samples in order to determine the media start time, - // we have the start time from last time we loaded. - // FinishDecodeFirstFrame will be launched upon completion of the seek when - // we have data ready to play. - MOZ_ASSERT(mQueuedSeek.Exists() && mSentFirstFrameLoadedEvent, - "Return from dormant must have queued seek"); - - if (mQueuedSeek.Exists()) { - InitiateSeek(Move(mQueuedSeek)); - return; - } + // Handle the pending seek now if we've decoded first frames. Otherwise it + // will be handled after decoding first frames. + if (mSentFirstFrameLoadedEvent && mQueuedSeek.Exists()) { + InitiateSeek(Move(mQueuedSeek)); + return; } if (CheckIfDecodeComplete()) { SetState(DECODER_STATE_COMPLETED); return; } mDecodeStartTime = TimeStamp::Now(); @@ -1506,18 +1497,22 @@ MediaDecoderStateMachine::Seek(SeekTarge if (aTarget.IsNextFrame() && !HasVideo()) { DECODER_WARN("Ignore a NextFrameSeekTask on a media file without video track."); return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__); } MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA, "We should have got duration already"); - if (mState < DECODER_STATE_DECODING || - (IsDecodingFirstFrame() && !mReader->ForceZeroStartTime())) { + // Can't seek until the start time is known. + bool hasStartTime = mSentFirstFrameLoadedEvent || mReader->ForceZeroStartTime(); + // Can't seek when state is WAIT_FOR_CDM or DORMANT. + bool stateAllowed = mState >= DECODER_STATE_DECODING; + + if (!stateAllowed || !hasStartTime) { DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek"); mQueuedSeek.RejectIfExists(__func__); mQueuedSeek.mTarget = aTarget; return mQueuedSeek.mPromise.Ensure(__func__); } mQueuedSeek.RejectIfExists(__func__); DECODER_LOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds()); @@ -1901,18 +1896,18 @@ bool MediaDecoderStateMachine::HasLowUnd { MOZ_ASSERT(OnTaskQueue()); return HasLowUndecodedData(mLowDataThresholdUsecs); } bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs) { MOZ_ASSERT(OnTaskQueue()); - NS_ASSERTION(mState >= DECODER_STATE_DECODING && !IsDecodingFirstFrame(), - "Must have loaded first frame for mBuffered to be valid"); + MOZ_ASSERT(mState >= DECODER_STATE_DECODING && mSentFirstFrameLoadedEvent, + "Must have loaded first frame for mBuffered to be valid"); // If we don't have a duration, mBuffered is probably not going to have // a useful buffered range. Return false here so that we don't get stuck in // buffering mode for live streams. if (Duration().IsInfinite()) { return false; } @@ -2069,22 +2064,16 @@ MediaDecoderStateMachine::EnqueueFirstFr : MediaDecoderEventVisibility::Observable; self->mFirstFrameLoadedEvent.Notify( nsAutoPtr<MediaInfo>(new MediaInfo(self->mInfo)), visibility); }, // Reject []() { MOZ_CRASH("Should not reach"); })); } -bool -MediaDecoderStateMachine::IsDecodingFirstFrame() -{ - return mState == DECODER_STATE_DECODING && mDecodingFirstFrame; -} - void MediaDecoderStateMachine::FinishDecodeFirstFrame() { MOZ_ASSERT(OnTaskQueue()); DECODER_LOG("FinishDecodeFirstFrame"); if (!mSentFirstFrameLoadedEvent) { mMediaSink->Redraw(mInfo.mVideo); @@ -2101,19 +2090,18 @@ MediaDecoderStateMachine::FinishDecodeFi // Get potentially updated metadata mReader->ReadUpdatedMetadata(&mInfo); if (!mNotifyMetadataBeforeFirstFrame) { // If we didn't have duration and/or start time before, we should now. EnqueueLoadedMetadataEvent(); } + EnqueueFirstFrameLoadedEvent(); - - mDecodingFirstFrame = false; } void MediaDecoderStateMachine::SeekCompleted() { MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(mState == DECODER_STATE_SEEKING); @@ -2168,19 +2156,21 @@ MediaDecoderStateMachine::SeekCompleted( // Reset the MediaDecoderReaderWrapper's callbask. DiscardSeekTaskIfExist(); // NOTE: Discarding the mSeekTask must be done before here. The following code // might ask the MediaDecoderReaderWrapper to request media data, however, the // SeekTask::Discard() will ask MediaDecoderReaderWrapper to discard media // data requests. - if (mDecodingFirstFrame) { - // We were resuming from dormant, or initiated a seek early. - // We can fire loadeddata now. + // Notify FirstFrameLoaded now if we haven't since we've decoded some data + // for readyState to transition to HAVE_CURRENT_DATA and fire 'loadeddata'. + if (!mSentFirstFrameLoadedEvent) { + // Only MSE can start seeking before finishing decoding first frames. + MOZ_ASSERT(mReader->ForceZeroStartTime()); FinishDecodeFirstFrame(); } // Ensure timestamps are up to date. UpdatePlaybackPositionInternal(newCurrentTime); // Try to decode another frame to detect if we're at the end... DECODER_LOG("Seek completed, mCurrentPosition=%lld", mCurrentPosition.Ref()); @@ -2268,19 +2258,18 @@ nsresult MediaDecoderStateMachine::RunSt switch (mState) { case DECODER_STATE_SHUTDOWN: case DECODER_STATE_DORMANT: case DECODER_STATE_WAIT_FOR_CDM: case DECODER_STATE_DECODING_METADATA: return NS_OK; case DECODER_STATE_DECODING: { - if (IsDecodingFirstFrame()) { - // We haven't completed decoding our first frames, we can't start - // playback yet. + // Can't start playback until having decoded first frames. + if (!mSentFirstFrameLoadedEvent) { return NS_OK; } if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING && IsPlaying()) { // We're playing, but the element/decoder is in paused state. Stop // playing! StopPlayback(); } @@ -2487,17 +2476,17 @@ MediaDecoderStateMachine::UpdatePlayback } void MediaDecoderStateMachine::UpdateNextFrameStatus() { MOZ_ASSERT(OnTaskQueue()); MediaDecoderOwner::NextFrameStatus status; const char* statusString; - if (mState <= DECODER_STATE_WAIT_FOR_CDM || IsDecodingFirstFrame()) { + if (mState < DECODER_STATE_DECODING || !mSentFirstFrameLoadedEvent) { status = MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; statusString = "NEXT_FRAME_UNAVAILABLE"; } else if (IsBuffering()) { status = MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING; statusString = "NEXT_FRAME_UNAVAILABLE_BUFFERING"; } else if (IsSeeking()) { status = MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING; statusString = "NEXT_FRAME_UNAVAILABLE_SEEKING"; @@ -2825,22 +2814,22 @@ MediaDecoderStateMachine::DumpDebugInfo( MOZ_ASSERT(NS_IsMainThread()); // It is fine to capture a raw pointer here because MediaDecoder only call // this function before shutdown begins. nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () { mMediaSink->DumpDebugInfo(); DUMP_LOG( "GetMediaTime=%lld GetClock=%lld mMediaSink=%p " - "mState=%s mPlayState=%d mDecodingFirstFrame=%d IsPlaying=%d " + "mState=%s mPlayState=%d mSentFirstFrameLoadedEvent=%d IsPlaying=%d " "mAudioStatus=%s mVideoStatus=%s mDecodedAudioEndTime=%lld mDecodedVideoEndTime=%lld " "mIsAudioPrerolling=%d mIsVideoPrerolling=%d " "mAudioCompleted=%d mVideoCompleted=%d", GetMediaTime(), mMediaSink->IsStarted() ? GetClock() : -1, mMediaSink.get(), - ToStateStr(), mPlayState.Ref(), mDecodingFirstFrame, IsPlaying(), + ToStateStr(), mPlayState.Ref(), mSentFirstFrameLoadedEvent, IsPlaying(), AudioRequestStatus(), VideoRequestStatus(), mDecodedAudioEndTime, mDecodedVideoEndTime, mIsAudioPrerolling, mIsVideoPrerolling, mAudioCompleted.Ref(), mVideoCompleted.Ref()); }); OwnerThread()->DispatchStateChange(r.forget()); } void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
--- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -562,18 +562,17 @@ protected: void OnMetadataRead(MetadataHolder* aMetadata); void OnMetadataNotRead(ReadMetadataFailureReason aReason); // Checks whether we're finished decoding first audio and/or video packets. // If so will trigger firing loadeddata event. // If there are any queued seek, will change state to DECODER_STATE_SEEKING // and return true. bool MaybeFinishDecodeFirstFrame(); - // Return true if we are currently decoding the first frames. - bool IsDecodingFirstFrame(); + void FinishDecodeFirstFrame(); // Completes the seek operation, moves onto the next appropriate state. void SeekCompleted(); // Queries our state to see whether the decode has finished for all streams. bool CheckIfDecodeComplete(); @@ -880,28 +879,24 @@ private: nsAutoPtr<MetadataTags> mMetadataTags; mozilla::MediaMetadataManager mMetadataManager; // Track our request to update the buffered ranges MozPromiseRequestHolder<MediaDecoderReader::BufferedUpdatePromise> mBufferedUpdateRequest; - // True if we need to call FinishDecodeFirstFrame() upon frame decoding - // succeeding. - bool mDecodingFirstFrame; - // True if we are back from DECODER_STATE_DORMANT state and // LoadedMetadataEvent was already sent. bool mSentLoadedMetadataEvent; - // True if we are back from DECODER_STATE_DORMANT state and - // FirstFrameLoadedEvent was already sent, then we can skip - // SetStartTime because the mStartTime already set before. Also we don't need - // to decode any audio/video since the MediaDecoder will trigger a seek - // operation soon. + + // True if we've decoded first frames (thus having the start time) and + // notified the FirstFrameLoaded event. Note we can't initiate seek until the + // start time is known which happens when the first frames are decoded or we + // are playing an MSE stream (the start time is always assumed 0). bool mSentFirstFrameLoadedEvent; bool mSentPlaybackEndedEvent; // True if video decoding is suspended. bool mVideoDecodeSuspended; // Track enabling video decode suspension via timer
--- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -57,19 +57,19 @@ TrackTypeToStr(TrackInfo::TrackType aTra } } MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, MediaDataDemuxer* aDemuxer, VideoFrameContainer* aVideoFrameContainer, layers::LayersBackend aLayersBackend) : MediaDecoderReader(aDecoder) - , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2), + , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-max-decode-error", 3)) - , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2), + , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-max-decode-error", 2)) , mDemuxer(aDemuxer) , mDemuxerInitDone(false) , mLastReportedNumDecodedFrames(0) , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe) , mLayersBackendType(aLayersBackend) , mInitDone(false) , mIsEncrypted(false) @@ -557,17 +557,17 @@ MediaFormatReader::RequestVideoData(bool if (!mVideo.HasInternalSeekPending() && ShouldSkip(aSkipToNextKeyframe, timeThreshold)) { RefPtr<MediaDataPromise> p = mVideo.EnsurePromise(__func__); SkipVideoDemuxToNextKeyFrame(timeThreshold); return p; } RefPtr<MediaDataPromise> p = mVideo.EnsurePromise(__func__); - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); return p; } void MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure) { MOZ_ASSERT(OnTaskQueue()); @@ -601,17 +601,16 @@ MediaFormatReader::OnDemuxFailed(TrackTy MOZ_ASSERT(false); break; } } void MediaFormatReader::DoDemuxVideo() { - // TODO Use DecodeAhead value rather than 1. mVideo.mDemuxRequest.Begin(mVideo.mTrackDemuxer->GetSamples(1) ->Then(OwnerThread(), __func__, this, &MediaFormatReader::OnVideoDemuxCompleted, &MediaFormatReader::OnVideoDemuxFailed)); } void MediaFormatReader::OnVideoDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) @@ -652,25 +651,24 @@ MediaFormatReader::RequestAudioData() } if (mShutdown) { NS_WARNING("RequestAudioData on shutdown MediaFormatReader!"); return MediaDataPromise::CreateAndReject(CANCELED, __func__); } RefPtr<MediaDataPromise> p = mAudio.EnsurePromise(__func__); - NotifyDecodingRequested(TrackInfo::kAudioTrack); + ScheduleUpdate(TrackInfo::kAudioTrack); return p; } void MediaFormatReader::DoDemuxAudio() { - // TODO Use DecodeAhead value rather than 1. mAudio.mDemuxRequest.Begin(mAudio.mTrackDemuxer->GetSamples(1) ->Then(OwnerThread(), __func__, this, &MediaFormatReader::OnAudioDemuxCompleted, &MediaFormatReader::OnAudioDemuxFailed)); } void MediaFormatReader::OnAudioDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) @@ -701,17 +699,17 @@ MediaFormatReader::NotifyNewOutput(Track } void MediaFormatReader::NotifyInputExhausted(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); LOGV("Decoder has requested more %s data", TrackTypeToStr(aTrack)); auto& decoder = GetDecoderData(aTrack); - decoder.mInputExhausted = true; + decoder.mDecodePending = false; ScheduleUpdate(aTrack); } void MediaFormatReader::NotifyDrainComplete(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); auto& decoder = GetDecoderData(aTrack); @@ -750,43 +748,31 @@ void MediaFormatReader::NotifyEndOfStream(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); auto& decoder = GetDecoderData(aTrack); decoder.mDemuxEOS = true; ScheduleUpdate(aTrack); } -void -MediaFormatReader::NotifyDecodingRequested(TrackType aTrack) -{ - MOZ_ASSERT(OnTaskQueue()); - auto& decoder = GetDecoderData(aTrack); - decoder.mDecodingRequested = true; - ScheduleUpdate(aTrack); -} - bool MediaFormatReader::NeedInput(DecoderData& aDecoder) { - // We try to keep a few more compressed samples input than decoded samples - // have been output, provided the state machine has requested we send it a - // decoded sample. To account for H.264 streams which may require a longer - // run of input than we input, decoders fire an "input exhausted" callback, - // which overrides our "few more samples" threshold. + // To account for H.264 streams which may require a longer + // run of input than we input, decoders fire an "input exhausted" callback. + // The decoder will not be fed a new raw sample until InputExhausted + // has been called. return + (aDecoder.HasPromise() || aDecoder.mTimeThreshold.isSome()) && !aDecoder.HasPendingDrain() && !aDecoder.HasFatalError() && - aDecoder.mDecodingRequested && !aDecoder.mDemuxRequest.Exists() && + !aDecoder.mOutput.Length() && !aDecoder.HasInternalSeekPending() && - aDecoder.mOutput.Length() <= aDecoder.mDecodeAhead && - (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() || - aDecoder.mTimeThreshold.isSome() || - aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput <= aDecoder.mDecodeAhead); + !aDecoder.mDecodePending; } void MediaFormatReader::ScheduleUpdate(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); if (mShutdown) { return; @@ -927,16 +913,17 @@ MediaFormatReader::DecodeDemuxedSamples( MediaRawData* aSample) { MOZ_ASSERT(OnTaskQueue()); auto& decoder = GetDecoderData(aTrack); if (NS_FAILED(decoder.mDecoder->Input(aSample))) { LOG("Unable to pass frame to decoder"); return false; } + decoder.mDecodePending = true; return true; } void MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, AbstractMediaDecoder::AutoNotifyDecoded& aA) { MOZ_ASSERT(OnTaskQueue()); @@ -1002,17 +989,17 @@ MediaFormatReader::HandleDemuxedSamples( decoder.mLastStreamSourceID = info->GetID(); decoder.mNextStreamSourceID.reset(); // Reset will clear our array of queued samples. So make a copy now. nsTArray<RefPtr<MediaRawData>> samples{decoder.mQueuedSamples}; Reset(aTrack); decoder.ShutdownDecoder(); if (sample->mKeyframe) { decoder.mQueuedSamples.AppendElements(Move(samples)); - NotifyDecodingRequested(aTrack); + ScheduleUpdate(aTrack); } else { TimeInterval time = TimeInterval(TimeUnit::FromMicroseconds(sample->mTime), TimeUnit::FromMicroseconds(sample->GetEndTime())); InternalSeekTarget seekTarget = decoder.mTimeThreshold.refOr(InternalSeekTarget(time, false)); LOG("Stream change occurred on a non-keyframe. Seeking to:%lld", sample->mTime); @@ -1040,19 +1027,16 @@ MediaFormatReader::HandleDemuxedSamples( decoder.mQueuedSamples.RemoveElementAt(0); if (mDemuxOnly) { // If demuxed-only case, ReturnOutput will resolve with one demuxed data. // Then we should stop doing the iteration. return; } samplesPending = true; } - - // We have serviced the decoder's request for more data. - decoder.mInputExhausted = false; } void MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTarget) { MOZ_ASSERT(OnTaskQueue()); LOG("%s internal seek to %f", TrackTypeToStr(aTrack), aTarget.Time().ToSeconds()); @@ -1066,17 +1050,17 @@ MediaFormatReader::InternalSeek(TrackTyp ->Then(OwnerThread(), __func__, [self, aTrack] (media::TimeUnit aTime) { auto& decoder = self->GetDecoderData(aTrack); decoder.mSeekRequest.Complete(); MOZ_ASSERT(decoder.mTimeThreshold, "Seek promise must be disconnected when timethreshold is reset"); decoder.mTimeThreshold.ref().mHasSeeked = true; self->SetVideoDecodeThreshold(); - self->NotifyDecodingRequested(aTrack); + self->ScheduleUpdate(aTrack); }, [self, aTrack] (DemuxerFailureReason aResult) { auto& decoder = self->GetDecoderData(aTrack); decoder.mSeekRequest.Complete(); switch (aResult) { case DemuxerFailureReason::WAITING_FOR_DATA: self->NotifyWaitingForData(aTrack); break; @@ -1270,16 +1254,17 @@ MediaFormatReader::Update(TrackType aTra if (decoder.mNeedDraining) { DrainDecoder(aTrack); return; } if (decoder.mError && decoder.mError.ref() == MediaDataDecoderError::DECODE_ERROR) { + decoder.mDecodePending = false; decoder.mError.reset(); if (++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) { NotifyError(aTrack); return; } LOG("%s decoded error count %d", TrackTypeToStr(aTrack), decoder.mNumOfConsecutiveError); media::TimeUnit nextKeyframe; @@ -1287,21 +1272,21 @@ MediaFormatReader::Update(TrackType aTra NS_SUCCEEDED(decoder.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe))) { SkipVideoDemuxToNextKeyFrame(decoder.mLastSampleTime.refOr(TimeInterval()).Length()); return; } } bool needInput = NeedInput(decoder); - LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u", - TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted, + LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d promise:%d sid:%u", + TrackTypeToStr(aTrack), needInput, needOutput, decoder.mDecodePending, decoder.mNumSamplesInput, decoder.mNumSamplesOutput, uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()), - decoder.mWaitingForData, !decoder.HasPromise(), decoder.mLastStreamSourceID); + decoder.mWaitingForData, decoder.HasPromise(), decoder.mLastStreamSourceID); if (decoder.mWaitingForData && (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) { // Nothing more we can do at present. LOGV("Still waiting for data."); return; } @@ -1572,17 +1557,17 @@ void MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped) { MOZ_ASSERT(OnTaskQueue()); LOG("Skipping succeeded, skipped %u frames", aSkipped); mSkipRequest.Complete(); VideoSkipReset(aSkipped); - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); } void MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure) { MOZ_ASSERT(OnTaskQueue()); LOG("Skipping failed, skipped %u frames", aFailure.mSkipped); mSkipRequest.Complete(); @@ -1590,17 +1575,17 @@ MediaFormatReader::OnVideoSkipFailed(Med switch (aFailure.mFailure) { case DemuxerFailureReason::END_OF_STREAM: MOZ_FALLTHROUGH; case DemuxerFailureReason::WAITING_FOR_DATA: // Some frames may have been output by the decoder since we initiated the // videoskip process and we know they would be late. DropDecodedSamples(TrackInfo::kVideoTrack); // We can't complete the skip operation, will just service a video frame // normally. - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); break; case DemuxerFailureReason::CANCELED: MOZ_FALLTHROUGH; case DemuxerFailureReason::SHUTDOWN: if (mVideo.HasPromise()) { mVideo.RejectPromise(CANCELED, __func__); } break; default: @@ -2008,22 +1993,21 @@ MediaFormatReader::GetMozDebugReaderData MonitorAutoLock mon(mVideo.mMonitor); videoName = mVideo.mDescription; } result += nsPrintfCString("audio decoder: %s\n", audioName); result += nsPrintfCString("audio frames decoded: %lld\n", mAudio.mNumSamplesOutputTotal); if (HasAudio()) { - result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", NeedInput(mAudio), mAudio.HasPromise(), - mAudio.mInputExhausted, + mAudio.mDecodePending, mAudio.mDemuxRequest.Exists(), int(mAudio.mQueuedSamples.Length()), - mAudio.mDecodingRequested, mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().Time().ToSeconds() : -1.0, mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().mHasSeeked : -1, mAudio.mNumSamplesInput, mAudio.mNumSamplesOutput, unsigned(size_t(mAudio.mSizeOfQueue)), @@ -2032,22 +2016,21 @@ MediaFormatReader::GetMozDebugReaderData } result += nsPrintfCString("video decoder: %s\n", videoName); result += nsPrintfCString("hardware video decoding: %s\n", VideoIsHardwareAccelerated() ? "enabled" : "disabled"); result += nsPrintfCString("video frames decoded: %lld (skipped:%lld)\n", mVideo.mNumSamplesOutputTotal, mVideo.mNumSamplesSkippedTotal); if (HasVideo()) { - result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", NeedInput(mVideo), mVideo.HasPromise(), - mVideo.mInputExhausted, + mVideo.mDecodePending, mVideo.mDemuxRequest.Exists(), int(mVideo.mQueuedSamples.Length()), - mVideo.mDecodingRequested, mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().Time().ToSeconds() : -1.0, mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().mHasSeeked : -1, mVideo.mNumSamplesInput, mVideo.mNumSamplesOutput, unsigned(size_t(mVideo.mSizeOfQueue)), @@ -2075,14 +2058,14 @@ MediaFormatReader::SetBlankDecode(TrackT if (decoder.mIsBlankDecode == aIsBlankDecode) { return; } decoder.mIsBlankDecode = aIsBlankDecode; decoder.Flush(); decoder.ShutdownDecoder(); - NotifyDecodingRequested(TrackInfo::kVideoTrack); // Calls ScheduleUpdate(). + ScheduleUpdate(TrackInfo::kVideoTrack); return; } } // namespace mozilla
--- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -164,17 +164,16 @@ private: // Drain the current decoder. void DrainDecoder(TrackType aTrack); void NotifyNewOutput(TrackType aTrack, MediaData* aSample); void NotifyInputExhausted(TrackType aTrack); void NotifyDrainComplete(TrackType aTrack); void NotifyError(TrackType aTrack, MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR); void NotifyWaitingForData(TrackType aTrack); void NotifyEndOfStream(TrackType aTrack); - void NotifyDecodingRequested(TrackType aTrack); void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData); // Initializes mLayersBackendType if possible. void InitLayersBackendType(); // DecoderCallback proxies the MediaDataDecoderCallback calls to these // functions. @@ -226,31 +225,28 @@ private: private: MediaFormatReader* mReader; TrackType mType; }; struct DecoderData { DecoderData(MediaFormatReader* aOwner, MediaData::Type aType, - uint32_t aDecodeAhead, uint32_t aNumOfMaxError) : mOwner(aOwner) , mType(aType) , mMonitor("DecoderData") , mDescription("shutdown") - , mDecodeAhead(aDecodeAhead) , mUpdateScheduled(false) , mDemuxEOS(false) , mWaitingForData(false) , mReceivedNewData(false) , mDecoderInitialized(false) - , mDecodingRequested(false) , mOutputRequested(false) - , mInputExhausted(false) + , mDecodePending(false) , mNeedDraining(false) , mDraining(false) , mDrainComplete(false) , mNumOfConsecutiveError(0) , mMaxConsecutiveError(aNumOfMaxError) , mNumSamplesInput(0) , mNumSamplesOutput(0) , mNumSamplesOutputTotal(0) @@ -283,17 +279,16 @@ private: if (mDecoder) { mDecoder->Shutdown(); } mDescription = "shutdown"; mDecoder = nullptr; } // Only accessed from reader's task queue. - uint32_t mDecodeAhead; bool mUpdateScheduled; bool mDemuxEOS; bool mWaitingForData; bool mReceivedNewData; // Pending seek. MozPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mSeekRequest; @@ -307,21 +302,24 @@ private: return !mWaitingPromise.IsEmpty(); } // MediaDataDecoder handler's variables. // Decoder initialization promise holder. MozPromiseRequestHolder<MediaDataDecoder::InitPromise> mInitPromise; // False when decoder is created. True when decoder Init() promise is resolved. bool mDecoderInitialized; - // Set when decoding can proceed. It is reset when a decoding promise is - // rejected or prior a seek operation. - bool mDecodingRequested; bool mOutputRequested; - bool mInputExhausted; + // Set to true once the MediaDataDecoder has been fed a compressed sample. + // No more sample will be passed to the decoder while true. + // mDecodePending is reset when: + // 1- The decoder returns a sample + // 2- The decoder calls InputExhausted + // 3- The decoder is Flushed or Reset. + bool mDecodePending; bool mNeedDraining; bool mDraining; bool mDrainComplete; bool HasPendingDrain() const { return mDraining || mDrainComplete; } @@ -371,19 +369,18 @@ private: // Flush the decoder if present and reset decoding related data. // Decoding will be suspended until mInputRequested is set again. // Following a flush, the decoder is ready to accept any new data. void Flush() { if (mDecoder) { mDecoder->Flush(); } - mDecodingRequested = false; mOutputRequested = false; - mInputExhausted = false; + mDecodePending = false; mOutput.Clear(); mNumSamplesInput = 0; mNumSamplesOutput = 0; mSizeOfQueue = 0; mDraining = false; mDrainComplete = false; } @@ -392,20 +389,19 @@ private: // Decoding will be suspended until mInputRequested is set again. // The track demuxer is *not* reset. void ResetState() { MOZ_ASSERT(mOwner->OnTaskQueue()); mDemuxEOS = false; mWaitingForData = false; mQueuedSamples.Clear(); - mDecodingRequested = false; mOutputRequested = false; - mInputExhausted = false; mNeedDraining = false; + mDecodePending = false; mDraining = false; mDrainComplete = false; mTimeThreshold.reset(); mLastSampleTime.reset(); mOutput.Clear(); mNumSamplesInput = 0; mNumSamplesOutput = 0; mSizeOfQueue = 0; @@ -436,19 +432,18 @@ private: bool mIsBlankDecode; }; class DecoderDataWithPromise : public DecoderData { public: DecoderDataWithPromise(MediaFormatReader* aOwner, MediaData::Type aType, - uint32_t aDecodeAhead, uint32_t aNumOfMaxError) - : DecoderData(aOwner, aType, aDecodeAhead, aNumOfMaxError) + : DecoderData(aOwner, aType, aNumOfMaxError) , mHasPromise(false) {} bool HasPromise() const override { return mHasPromise; } @@ -467,17 +462,16 @@ private: mHasPromise = false; } void RejectPromise(MediaDecoderReader::NotDecodedReason aReason, const char* aMethodName) override { MOZ_ASSERT(mOwner->OnTaskQueue()); mPromise.Reject(aReason, aMethodName); - mDecodingRequested = false; mHasPromise = false; } private: MozPromiseHolder<MediaDataPromise> mPromise; Atomic<bool> mHasPromise; };
--- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1072,16 +1072,35 @@ void MediaStreamGraph::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) { for (auto& listener : mAudioInputs) { listener->NotifyOutputData(this, aBuffer, aFrames, aRate, aChannels); } } +void +MediaStreamGraph::AssertOnGraphThreadOrNotRunning() const +{ + // either we're on the right thread (and calling CurrentDriver() is safe), + // or we're going to assert anyways, so don't cross-check CurrentDriver +#ifdef DEBUG + MediaStreamGraphImpl const * graph = + static_cast<MediaStreamGraphImpl const *>(this); + // if all the safety checks fail, assert we own the monitor + if (!graph->mDriver->OnThread()) { + if (!(graph->mDetectedNotRunning && + graph->mLifecycleState > MediaStreamGraphImpl::LIFECYCLE_RUNNING && + NS_IsMainThread())) { + graph->mMonitor.AssertCurrentThreadOwns(); + } + } +#endif +} + bool MediaStreamGraphImpl::ShouldUpdateMainThread() { if (mRealtime) { return true; } TimeStamp now = TimeStamp::Now();
--- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -1348,16 +1348,17 @@ public: * Media graph thread only. * Dispatches a runnable that will run on the main thread after all * main-thread stream state has been next updated. * Should only be called during MediaStreamListener callbacks or during * ProcessedMediaStream::ProcessInput(). */ virtual void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable) { + AssertOnGraphThreadOrNotRunning(); *mPendingUpdateRunnables.AppendElement() = aRunnable; } /** * Returns graph sample rate in Hz. */ TrackRate GraphRate() const { return mSampleRate; } @@ -1369,16 +1370,18 @@ public: /** * Data going to the speakers from the GraphDriver's DataCallback * to notify any listeners (for echo cancellation). */ void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels); + void AssertOnGraphThreadOrNotRunning() const; + protected: explicit MediaStreamGraph(TrackRate aSampleRate) : mSampleRate(aSampleRate) { MOZ_COUNT_CTOR(MediaStreamGraph); } virtual ~MediaStreamGraph() {
--- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -197,34 +197,18 @@ public: /** * Respond to CollectReports with sizes collected on the graph thread. */ static void FinishCollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, const nsTArray<AudioNodeSizes>& aAudioStreamSizes); - // The following methods run on the graph thread (or possibly the main thread if - // mLifecycleState > LIFECYCLE_RUNNING) - void AssertOnGraphThreadOrNotRunning() const - { - // either we're on the right thread (and calling CurrentDriver() is safe), - // or we're going to assert anyways, so don't cross-check CurrentDriver -#ifdef DEBUG - // if all the safety checks fail, assert we own the monitor - if (!mDriver->OnThread()) { - if (!(mDetectedNotRunning && - mLifecycleState > LIFECYCLE_RUNNING && - NS_IsMainThread())) { - mMonitor.AssertCurrentThreadOwns(); - } - } -#endif - } - + // The following methods run on the graph thread (or possibly the main thread + // if mLifecycleState > LIFECYCLE_RUNNING) void CollectSizesForMemoryReport( already_AddRefed<nsIHandleReportCallback> aHandleReport, already_AddRefed<nsISupports> aHandlerData); /** * Returns true if this MediaStreamGraph should keep running */ bool UpdateMainThreadState();
--- a/dom/media/flac/FlacFrameParser.cpp +++ b/dom/media/flac/FlacFrameParser.cpp @@ -127,17 +127,17 @@ FlacFrameParser::DecodeHeaderBlock(const uint32_t sampleRate = (blob >> 44) & BITMASK(20); if (!sampleRate) { return false; } uint32_t numChannels = ((blob >> 41) & BITMASK(3)) + 1; if (numChannels > FLAC_MAX_CHANNELS) { return false; } - uint32_t bps = ((blob >> 38) & BITMASK(5)) + 1; + uint32_t bps = ((blob >> 36) & BITMASK(5)) + 1; if (bps > 24) { return false; } mNumFrames = blob & BITMASK(36); mInfo.mMimeType = "audio/flac"; mInfo.mRate = sampleRate; mInfo.mChannels = numChannels;
--- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -170,16 +170,19 @@ public: virtual void Output(MediaData* aData) = 0; // Denotes an error in the decoding process. The reader will stop calling // the decoder. virtual void Error(MediaDataDecoderError aError) = 0; // Denotes that the last input sample has been inserted into the decoder, // and no more output can be produced unless more input is sent. + // A frame decoding session is completed once InputExhausted has been called. + // MediaDataDecoder::Input will not be called again until InputExhausted has + // been called. virtual void InputExhausted() = 0; virtual void DrainComplete() = 0; virtual void ReleaseMediaResources() {} virtual bool OnReaderTaskQueue() = 0;
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp +++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp @@ -91,21 +91,17 @@ private: } // Frames come out in DTS order but we need to output them in PTS order. mReorderQueue.Push(aData); while (mReorderQueue.Length() > mMaxRefFrames) { mCallback->Output(mReorderQueue.Pop().get()); } - - if (mReorderQueue.Length() <= mMaxRefFrames) { - mCallback->InputExhausted(); - } - + mCallback->InputExhausted(); } private: nsAutoPtr<BlankMediaDataCreator> mCreator; MediaDataDecoderCallback* mCallback; const uint32_t mMaxRefFrames; ReorderQueue mReorderQueue; TrackInfo::TrackType mType;
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp +++ b/dom/media/platforms/agnostic/OpusDecoder.cpp @@ -158,22 +158,19 @@ OpusDataDecoder::ProcessDecode(MediaRawD switch (err) { case DecodeError::FATAL_ERROR: mCallback->Error(MediaDataDecoderError::FATAL_ERROR); return; case DecodeError::DECODE_ERROR: mCallback->Error(MediaDataDecoderError::DECODE_ERROR); break; case DecodeError::DECODE_SUCCESS: + mCallback->InputExhausted(); break; } - - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } } OpusDataDecoder::DecodeError OpusDataDecoder::DoDecode(MediaRawData* aSample) { int64_t aDiscardPadding = 0; if (aSample->mExtraData) { aDiscardPadding = BigEndian::readInt64(aSample->mExtraData->Elements());
--- a/dom/media/platforms/agnostic/TheoraDecoder.cpp +++ b/dom/media/platforms/agnostic/TheoraDecoder.cpp @@ -197,17 +197,17 @@ void TheoraDecoder::ProcessDecode(MediaRawData* aSample) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); if (mIsFlushing) { return; } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { + } else { mCallback->InputExhausted(); } } nsresult TheoraDecoder::Input(MediaRawData* aSample) { MOZ_ASSERT(mCallback->OnReaderTaskQueue());
--- a/dom/media/platforms/agnostic/VPXDecoder.cpp +++ b/dom/media/platforms/agnostic/VPXDecoder.cpp @@ -187,17 +187,17 @@ void VPXDecoder::ProcessDecode(MediaRawData* aSample) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); if (mIsFlushing) { return; } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { + } else { mCallback->InputExhausted(); } } nsresult VPXDecoder::Input(MediaRawData* aSample) { MOZ_ASSERT(mCallback->OnReaderTaskQueue());
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp +++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp @@ -138,17 +138,17 @@ void VorbisDataDecoder::ProcessDecode(MediaRawData* aSample) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); if (mIsFlushing) { return; } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { + } else { mCallback->InputExhausted(); } } int VorbisDataDecoder::DoDecode(MediaRawData* aSample) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
--- a/dom/media/platforms/agnostic/WAVDecoder.cpp +++ b/dom/media/platforms/agnostic/WAVDecoder.cpp @@ -63,16 +63,18 @@ WaveDataDecoder::Init() return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__); } nsresult WaveDataDecoder::Input(MediaRawData* aSample) { if (!DoDecode(aSample)) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); + } else { + mCallback->InputExhausted(); } return NS_OK; } bool WaveDataDecoder::DoDecode(MediaRawData* aSample) { size_t aLength = aSample->Size();
--- a/dom/media/platforms/apple/AppleATDecoder.cpp +++ b/dom/media/platforms/apple/AppleATDecoder.cpp @@ -210,20 +210,17 @@ AppleATDecoder::SubmitSample(MediaRawDat if (NS_FAILED(DecodeSample(mQueuedSamples[i]))) { mQueuedSamples.Clear(); mCallback->Error(MediaDataDecoderError::DECODE_ERROR); return; } } mQueuedSamples.Clear(); } - - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } + mCallback->InputExhausted(); } nsresult AppleATDecoder::DecodeSample(MediaRawData* aSample) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); // Array containing the queued decoded audio frames, about to be output.
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp +++ b/dom/media/platforms/apple/AppleVTDecoder.cpp @@ -29,21 +29,19 @@ AppleVTDecoder::AppleVTDecoder(const Vid MediaDataDecoderCallback* aCallback, layers::ImageContainer* aImageContainer) : mExtraData(aConfig.mExtraData) , mCallback(aCallback) , mPictureWidth(aConfig.mImage.width) , mPictureHeight(aConfig.mImage.height) , mDisplayWidth(aConfig.mDisplay.width) , mDisplayHeight(aConfig.mDisplay.height) - , mQueuedSamples(0) , mTaskQueue(aTaskQueue) , mMaxRefFrames(mp4_demuxer::H264::ComputeMaxRefFrames(aConfig.mExtraData)) , mImageContainer(aImageContainer) - , mInputIncoming(0) , mIsShutDown(false) #ifdef MOZ_WIDGET_UIKIT , mUseSoftwareImages(true) #else , mUseSoftwareImages(false) #endif , mIsFlushing(false) , mMonitor("AppleVideoDecoder") @@ -83,34 +81,30 @@ AppleVTDecoder::Input(MediaRawData* aSam LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes", aSample, aSample->mTime, aSample->mDuration, aSample->mKeyframe ? " keyframe" : "", aSample->Size()); - mInputIncoming++; - mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>( this, &AppleVTDecoder::ProcessDecode, aSample)); return NS_OK; } nsresult AppleVTDecoder::Flush() { MOZ_ASSERT(mCallback->OnReaderTaskQueue()); mIsFlushing = true; nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(this, &AppleVTDecoder::ProcessFlush); SyncRunnable::DispatchToThread(mTaskQueue, runnable); mIsFlushing = false; - // All ProcessDecode() tasks should be done. - MOZ_ASSERT(mInputIncoming == 0); mSeekTargetThreshold.reset(); return NS_OK; } nsresult AppleVTDecoder::Drain() @@ -137,28 +131,21 @@ AppleVTDecoder::Shutdown() return NS_OK; } nsresult AppleVTDecoder::ProcessDecode(MediaRawData* aSample) { AssertOnTaskQueueThread(); - mInputIncoming--; - if (mIsFlushing) { return NS_OK; } auto rv = DoDecode(aSample); - // Ask for more data. - if (NS_SUCCEEDED(rv) && !mInputIncoming && mQueuedSamples <= mMaxRefFrames) { - LOG("%s task queue empty; requesting more data", GetDescriptionName()); - mCallback->InputExhausted(); - } return rv; } void AppleVTDecoder::ProcessShutdown() { if (mSession) { @@ -208,27 +195,25 @@ AppleVTDecoder::CreateAppleFrameRef(cons void AppleVTDecoder::DrainReorderedFrames() { MonitorAutoLock mon(mMonitor); while (!mReorderQueue.IsEmpty()) { mCallback->Output(mReorderQueue.Pop().get()); } - mQueuedSamples = 0; } void AppleVTDecoder::ClearReorderedFrames() { MonitorAutoLock mon(mMonitor); while (!mReorderQueue.IsEmpty()) { mReorderQueue.Pop(); } - mQueuedSamples = 0; } void AppleVTDecoder::SetSeekThreshold(const media::TimeUnit& aTime) { LOG("SetSeekThreshold %lld", aTime.ToMicroseconds()); mSeekTargetThreshold = Some(aTime); } @@ -283,26 +268,20 @@ AppleVTDecoder::OutputFrame(CVPixelBuffe LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s", aFrameRef.byte_offset, aFrameRef.decode_timestamp.ToMicroseconds(), aFrameRef.composition_timestamp.ToMicroseconds(), aFrameRef.duration.ToMicroseconds(), aFrameRef.is_sync_point ? " keyframe" : "" ); - if (mQueuedSamples > mMaxRefFrames) { - // We had stopped requesting more input because we had received too much at - // the time. We can ask for more once again. + if (!aImage) { + // Image was dropped by decoder or none return yet. + // We need more input to continue. mCallback->InputExhausted(); - } - MOZ_ASSERT(mQueuedSamples); - mQueuedSamples--; - - if (!aImage) { - // Image was dropped by decoder. return NS_OK; } bool useNullSample = false; if (mSeekTargetThreshold.isSome()) { if ((aFrameRef.composition_timestamp + aFrameRef.duration) < mSeekTargetThreshold.ref()) { useNullSample = true; } else { @@ -405,19 +384,20 @@ AppleVTDecoder::OutputFrame(CVPixelBuffe mCallback->Error(MediaDataDecoderError::FATAL_ERROR); return NS_ERROR_FAILURE; } // Frames come out in DTS order but we need to output them // in composition order. MonitorAutoLock mon(mMonitor); mReorderQueue.Push(data); - while (mReorderQueue.Length() > mMaxRefFrames) { + if (mReorderQueue.Length() > mMaxRefFrames) { mCallback->Output(mReorderQueue.Pop().get()); } + mCallback->InputExhausted(); LOG("%llu decoded frames queued", static_cast<unsigned long long>(mReorderQueue.Length())); return NS_OK; } nsresult AppleVTDecoder::WaitForAsynchronousFrames() @@ -475,18 +455,16 @@ AppleVTDecoder::DoDecode(MediaRawData* a } CMSampleTimingInfo timestamp = TimingInfoFromSample(aSample); rv = CMSampleBufferCreate(kCFAllocatorDefault, block, true, 0, 0, mFormat, 1, 1, ×tamp, 0, NULL, sample.receive()); if (rv != noErr) { NS_ERROR("Couldn't create CMSampleBuffer"); return NS_ERROR_FAILURE; } - mQueuedSamples++; - VTDecodeFrameFlags decodeFlags = kVTDecodeFrame_EnableAsynchronousDecompression; rv = VTDecompressionSessionDecodeFrame(mSession, sample, decodeFlags, CreateAppleFrameRef(aSample), &infoFlags); if (rv != noErr && !(infoFlags & kVTDecodeInfo_FrameDropped)) {
--- a/dom/media/platforms/apple/AppleVTDecoder.h +++ b/dom/media/platforms/apple/AppleVTDecoder.h @@ -85,35 +85,27 @@ private: const RefPtr<MediaByteBuffer> mExtraData; MediaDataDecoderCallback* mCallback; const uint32_t mPictureWidth; const uint32_t mPictureHeight; const uint32_t mDisplayWidth; const uint32_t mDisplayHeight; - // Number of times a sample was queued via Input(). Will be decreased upon - // the decoder's callback being invoked. - // This is used to calculate how many frames has been buffered by the decoder. - Atomic<uint32_t> mQueuedSamples; - // Method to set up the decompression session. nsresult InitializeSession(); nsresult WaitForAsynchronousFrames(); CFDictionaryRef CreateDecoderSpecification(); CFDictionaryRef CreateDecoderExtensions(); // Method to pass a frame to VideoToolbox for decoding. nsresult DoDecode(MediaRawData* aSample); const RefPtr<TaskQueue> mTaskQueue; const uint32_t mMaxRefFrames; const RefPtr<layers::ImageContainer> mImageContainer; - // Increased when Input is called, and decreased when ProcessFrame runs. - // Reaching 0 indicates that there's no pending Input. - Atomic<uint32_t> mInputIncoming; Atomic<bool> mIsShutDown; const bool mUseSoftwareImages; // Set on reader/decode thread calling Flush() to indicate that output is // not required and so input samples on mTaskQueue need not be processed. // Cleared on mTaskQueue in ProcessDrain(). Atomic<bool> mIsFlushing; // Protects mReorderQueue.
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp @@ -128,16 +128,17 @@ FFmpegAudioDecoder<LIBAV_VER>::DoDecode( if (!PrepareFrame()) { NS_WARNING("FFmpeg audio decoder failed to allocate frame."); return DecodeResult::FATAL_ERROR; } int64_t samplePosition = aSample->mOffset; media::TimeUnit pts = media::TimeUnit::FromMicroseconds(aSample->mTime); + bool didOutput = false; while (packet.size > 0) { int decoded; int bytesConsumed = mLib->avcodec_decode_audio4(mCodecContext, mFrame, &decoded, &packet); if (bytesConsumed < 0) { NS_WARNING("FFmpeg audio decoder error."); @@ -176,28 +177,29 @@ FFmpegAudioDecoder<LIBAV_VER>::DoDecode( RefPtr<AudioData> data = new AudioData(samplePosition, pts.ToMicroseconds(), duration.ToMicroseconds(), mFrame->nb_samples, Move(audio), numChannels, samplingRate); mCallback->Output(data); + didOutput = true; pts += duration; if (!pts.IsValid()) { NS_WARNING("Invalid count of accumulated audio samples"); return DecodeResult::DECODE_ERROR; } } packet.data += bytesConsumed; packet.size -= bytesConsumed; samplePosition += bytesConsumed; } - return DecodeResult::DECODE_FRAME; + return didOutput ? DecodeResult::DECODE_FRAME : DecodeResult::DECODE_NO_FRAME; } void FFmpegAudioDecoder<LIBAV_VER>::ProcessDrain() { ProcessFlush(); mCallback->DrainComplete(); }
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp @@ -112,20 +112,22 @@ FFmpegDataDecoder<LIBAV_VER>::ProcessDec } switch (DoDecode(aSample)) { case DecodeResult::DECODE_ERROR: mCallback->Error(MediaDataDecoderError::DECODE_ERROR); break; case DecodeResult::FATAL_ERROR: mCallback->Error(MediaDataDecoderError::FATAL_ERROR); break; + case DecodeResult::DECODE_NO_FRAME: + case DecodeResult::DECODE_FRAME: + mCallback->InputExhausted(); + break; default: - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } + break; } } nsresult FFmpegDataDecoder<LIBAV_VER>::Input(MediaRawData* aSample) { mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>( this, &FFmpegDataDecoder::ProcessDecode, aSample));
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -144,19 +144,17 @@ WMFMediaDataDecoder::ProcessOutput() RefPtr<MediaData> output; HRESULT hr = S_OK; while (SUCCEEDED(hr = mMFTManager->Output(mLastStreamOffset, output)) && output) { mHasSuccessfulOutput = true; mCallback->Output(output); } if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } + mCallback->InputExhausted(); } else if (FAILED(hr)) { NS_WARNING("WMFMediaDataDecoder failed to output data"); mCallback->Error(MediaDataDecoderError::DECODE_ERROR); if (!mRecordedError) { SendTelemetry(hr); mRecordedError = true; } }
--- a/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py +++ b/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py @@ -10,19 +10,26 @@ from marionette import Marionette from marionette_driver import By, expected, Wait from marionette_driver.errors import TimeoutException, NoSuchElementException from video_puppeteer import VideoPuppeteer, VideoException from external_media_tests.utils import verbose_until class YouTubePuppeteer(VideoPuppeteer): """ - Wrapper around a YouTube #movie_player element. + Wrapper around a YouTube .html5-video-player element. - Partial reference: https://developers.google.com/youtube/js_api_reference. + Can be used with youtube videos or youtube videos at embedded URLS. E.g. + both https://www.youtube.com/watch?v=AbAACm1IQE0 and + https://www.youtube.com/embed/AbAACm1IQE0 should work. + + Using an embedded video has the advantage of not auto-playing more videos + while a test is running. + + Partial reference: https://developers.google.com/youtube/iframe_api_reference. This reference is useful for site-specific features such as interacting with ads, or accessing YouTube's debug data. """ _yt_player_state = { 'UNSTARTED': -1, 'ENDED': 0, 'PLAYING': 1, @@ -32,24 +39,26 @@ class YouTubePuppeteer(VideoPuppeteer): } _yt_player_state_name = {v: k for k, v in _yt_player_state.items()} _time_pattern = re.compile('(?P<minute>\d+):(?P<second>\d+)') def __init__(self, marionette, url, **kwargs): self.player = None super(YouTubePuppeteer, self).__init__(marionette, url, - video_selector='#movie_player video', + video_selector='.html5-video-player video', **kwargs) wait = Wait(self.marionette, timeout=30) with self.marionette.using_context(Marionette.CONTEXT_CONTENT): verbose_until(wait, self, - expected.element_present(By.ID, 'movie_player')) - self.player = self.marionette.find_element(By.ID, 'movie_player') - self.marionette.execute_script("log('#movie_player " + expected.element_present(By.CLASS_NAME, + 'html5-video-player')) + self.player = self.marionette.find_element(By.CLASS_NAME, + 'html5-video-player') + self.marionette.execute_script("log('.html5-video-player " "element obtained');") # When an ad is playing, self.player_duration indicates the duration # of the spliced-in ad stream, not the duration of the main video, so # we attempt to skip the ad first. for attempt in range(5): sleep(1) self.process_ad() if (self.ad_inactive and self.duration and not @@ -112,31 +121,31 @@ class YouTubePuppeteer(VideoPuppeteer): return loads(text) except ValueError: self.marionette.log('Error loading json: DebugText', level='DEBUG') def execute_yt_script(self, script): """ Execute JS script in content context with access to video element and - YouTube #movie_player element. + YouTube .html5-video-player element. :param script: script to be executed. :return: value returned by script """ with self.marionette.using_context(Marionette.CONTEXT_CONTENT): return self.marionette.execute_script(script, script_args=[self.video, self.player]) @property def playback_quality(self): """ - Please see https://developers.google.com/youtube/js_api_reference#Playback_quality + Please see https://developers.google.com/youtube/iframe_api_reference#Playback_quality for valid values. :return: A string with a valid value returned via YouTube. """ return self.execute_yt_script('return arguments[1].' 'wrappedJSObject.getPlaybackQuality();') @property @@ -171,29 +180,29 @@ class YouTubePuppeteer(VideoPuppeteer): return self.execute_yt_script('return arguments[1].' 'wrappedJSObject.getVideoUrl();') @property def player_state(self): """ :return: The YouTube state of the video. See - https://developers.google.com/youtube/js_api_reference#getPlayerState + https://developers.google.com/youtube/iframe_api_reference#getPlayerState for valid values. """ state = self.execute_yt_script('return arguments[1].' 'wrappedJSObject.getPlayerState();') return state @property def player_unstarted(self): """ This and the following properties are based on the player.getPlayerState() call - (https://developers.google.com/youtube/js_api_reference#Playback_status) + (https://developers.google.com/youtube/iframe_api_reference#Playback_status) :return: True if the video has not yet started. """ return self.player_state == self._yt_player_state['UNSTARTED'] @property def player_ended(self): """ @@ -235,17 +244,17 @@ class YouTubePuppeteer(VideoPuppeteer): return self.player_state == self._yt_player_state['CUED'] @property def ad_state(self): """ Get state of current ad. :return: Returns one of the constants listed in - https://developers.google.com/youtube/js_api_reference#Playback_status + https://developers.google.com/youtube/iframe_api_reference#Playback_status for an ad. """ # Note: ad_state is sometimes not accurate, due to some sort of lag? return self.execute_yt_script('return arguments[1].' 'wrappedJSObject.getAdState();') @property @@ -345,17 +354,17 @@ class YouTubePuppeteer(VideoPuppeteer): """ if self.ad_playing: self.marionette.log('Waiting while ad plays') sleep(10) else: # no ad playing return False if self.ad_skippable: - selector = '#movie_player .videoAdUiSkipContainer' + selector = '.html5-video-player .videoAdUiSkipContainer' wait = Wait(self.marionette, timeout=30) try: with self.marionette.using_context(Marionette.CONTEXT_CONTENT): wait.until(expected.element_displayed(By.CSS_SELECTOR, selector)) ad_button = self.marionette.find_element(By.CSS_SELECTOR, selector) ad_button.click() @@ -373,17 +382,17 @@ class YouTubePuppeteer(VideoPuppeteer): :return: ad duration in seconds, if currently displayed in player """ if not (self.ad_playing or self.player_measure_progress() == 0): return None # If the ad is not Flash... if (self.ad_playing and self.video_src.startswith('mediasource') and self.duration): return self.duration - selector = '#movie_player .videoAdUiAttribution' + selector = '.html5-media-player .videoAdUiAttribution' wait = Wait(self.marionette, timeout=5) try: with self.marionette.using_context(Marionette.CONTEXT_CONTENT): wait.until(expected.element_present(By.CSS_SELECTOR, selector)) countdown = self.marionette.find_element(By.CSS_SELECTOR, selector) ad_time = self._time_pattern.search(countdown.text) @@ -459,28 +468,28 @@ class YouTubePuppeteer(VideoPuppeteer): return False def __str__(self): messages = [super(YouTubePuppeteer, self).__str__()] if self.player: player_state = self._yt_player_state_name[self.player_state] ad_state = self._yt_player_state_name[self.ad_state] messages += [ - '#movie_player: {', + '.html5-media-player: {', '\tvideo id: {0},'.format(self.movie_id), '\tvideo_title: {0}'.format(self.movie_title), '\tcurrent_state: {0},'.format(player_state), '\tad_state: {0},'.format(ad_state), '\tplayback_quality: {0},'.format(self.playback_quality), '\tcurrent_time: {0},'.format(self.player_current_time), '\tduration: {0},'.format(self.player_duration), '}' ] else: - messages += ['\t#movie_player: None'] + messages += ['\t.html5-media-player: None'] return '\n'.join(messages) def playback_started(yt): """ Check whether playback has started. :param yt: YouTubePuppeteer
--- a/dom/media/test/external/external_media_tests/urls/default.ini +++ b/dom/media/test/external/external_media_tests/urls/default.ini @@ -1,9 +1,9 @@ -# short videos; no ads; max 5 minutes +# short videos; no ads; embedded; max 5 minutes # 0:12 -[https://youtu.be/AbAACm1IQE0] +[https://youtube.com/embed/AbAACm1IQE0?autoplay=1] # 2:18 -[https://www.youtube.com/watch?v=yOQQCoxs8-k] +[https://youtube.com/embed/yOQQCoxs8-k?autoplay=1] # 0:08 -[https://www.youtube.com/watch?v=1visYpIREUM] +[https://youtube.com/embed/1visYpIREUM?autoplay=1] # 2:09 -[https://www.youtube.com/watch?v=rjmuKV9BTkE] +[https://youtube.com/embed/rjmuKV9BTkE?autoplay=1]
--- a/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini @@ -1,14 +1,5 @@ -# all long videos; < 12 hours total -# 2:18:00 -[http://youtu.be/FLX64H5FYa8] -# 1:00:00 -[https://www.youtube.com/watch?v=AYYDshv8C4g] -# 1:10:00 -[https://www.youtube.com/watch?v=V0Vy4kYAPDk] -# 1:47:00 -[https://www.youtube.com/watch?v=bFtGE2C7Pxs] - - -# shutdownhang | WaitForSingleObjectEx | WaitForSingleObject | PR_Wait | nsThread::ProcessNextEvent(bool, bool*) | NS_ProcessNextEvent(nsIThread*, bool) | mozilla::MediaShutdownManager::Shutdown() -# 1:43:00 -[https://www.youtube.com/watch?v=BXMtXpmpXPU] +# a couple of very long videos, < 12 hours total +# 6:00:00 - can't embed due to copyright +[https://www.youtube.com/watch?v=5N8sUccRiTA] +# 2:09:00 +[https://www.youtube.com/embed/b6q5N16dje4?autoplay=1]
deleted file mode 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/long2-720.ini +++ /dev/null @@ -1,9 +0,0 @@ -# a couple of very long videos, < 12 hours total -# 6:00:00 -[https://www.youtube.com/watch?v=5N8sUccRiTA] -# 2:27:00 -[https://www.youtube.com/watch?v=NAVrm3wjzq8] -# 58:50 -[https://www.youtube.com/watch?v=uP1BBw3IYco] -# 2:09:00 -[https://www.youtube.com/watch?v=b6q5N16dje4]
rename from dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-720.ini rename to dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini --- a/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-720.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini @@ -1,8 +1,11 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + # videos from crashes, < 12 hours # hang | NtUserMessageCall | SendMessageW # 1:10:00 [https://www.youtube.com/watch?v=Ztie4DqeOak] # nsPluginInstanceOwner::GetDocument(nsIDocument**) # 22:40
rename from dom/media/test/external/external_media_tests/urls/youtube/long4-crashes-900.ini rename to dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini --- a/dom/media/test/external/external_media_tests/urls/youtube/long4-crashes-900.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini @@ -1,8 +1,11 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + # Total time: about 12-13 hours + unskippable ads #Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=50&platform=Windows&version=37.0&date=%3E2015-03-26 #Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dhang+%7C+NtUserMessageCall+%7C+SendMessageW&date=%3E2015-03-26 #Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3DOOM+%7C+small&date=%3E2015-03-26 #Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dmozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3AHandleError%28long%2C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3ASeverity%29+%7C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3AFailed%28long%2C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3ASeverity%29+%7C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3AUpdateRenderTarget%28%29&date=%3E2015-03-26
deleted file mode 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/massive-6000.ini +++ /dev/null @@ -1,15 +0,0 @@ -# very long test; 96-100 hours? -# 00:3:26 -[https://www.youtube.com/watch?v=7RMQksXpQSk] -# nyan cat 10 hours -[http://youtu.be/9bZkp7q19f0] -# 4:54:00 -[https://www.youtube.com/watch?v=jWlKjw3LBDk] -# 3:00:01 -[https://www.youtube.com/watch?v=ub9JUDS_6i8] -# 10 hours rick roll -[https://www.youtube.com/watch?v=BROWqjuTM0g] -# 24 hours -[https://www.youtube.com/watch?v=FvHiLLkPhQE] -# 2 hours -[https://www.youtube.com/watch?v=VmOuW5zTt9w
--- a/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini @@ -1,18 +1,18 @@ # mix of shorter/longer videos with/without ads, < 60 min -# 4:59 -[http://youtu.be/pWI8RB2dmfU] +# 4:59 - can't embed +[https://www.youtube.com/watch?v=pWI8RB2dmfU] # 0:46 ad at start -[http://youtu.be/6SFp1z7uA6g] +[https://www.youtube.com/embed/6SFp1z7uA6g?autoplay=1] # 0:58 ad at start -[http://youtu.be/Aebs62bX0dA] +[https://www.youtube.com/embed/Aebs62bX0dA?autoplay=1] # 1:43 ad -[https://www.youtube.com/watch?v=l5ODwR6FPRQ] -# 8:00 ad +[https://www.youtube.com/embed/l5ODwR6FPRQ?autoplay=1] +# 8:00 ad - can't embed [https://www.youtube.com/watch?v=KlyXNRrsk4A] # video with ad in beginning and in the middle 20:00 # https://bugzilla.mozilla.org/show_bug.cgi?id=1176815 -[https://www.youtube.com/watch?v=cht9Xq9suGg] +[https://www.youtube.com/embed/cht9Xq9suGg?autoplay=1] # 1:35 ad -[https://www.youtube.com/watch?v=orybDrUj4vA] -# 3:02 - ad -[https://youtu.be/tDDVAErOI5U] +[https://www.youtube.com/embed/orybDrUj4vA?autoplay=1] +# 3:02 ad +[https://www.youtube.com/embed/tDDVAErOI5U?autoplay=1]
deleted file mode 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/medium2-60.ini +++ /dev/null @@ -1,6 +0,0 @@ -# a few longer videos, < 60 min total -# 0:30:00 no ad -[https://www.youtube.com/watch?v=-qXxNPvqHtQ] -# 0:20:00 -[http://youtu.be/Fu2DcHzokew] -
deleted file mode 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/medium3-120.ini +++ /dev/null @@ -1,11 +0,0 @@ -# a few longer videos, < 120 min total -# video with ad in the middle -# 21:00 -[https://www.youtube.com/watch?v=cht9Xq9suGg] -# 16:00 -[https://www.youtube.com/watch?v=6Lm9EHhbJAY] -# 20:00 -[https://www.youtube.com/watch?v=8XQ1onjXJK0] -# 59:06 -[https://www.youtube.com/watch?v=kmpiY5kssU4] -
rename from dom/media/test/external/external_media_tests/urls/youtube/short0-10.ini rename to dom/media/test/external/external_media_tests/urls/youtube/short1-10.ini
deleted file mode 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/short1-15.ini +++ /dev/null @@ -1,5 +0,0 @@ -# 00:12 -[https://youtu.be/AbAACm1IQE0] -# longer video with ads; < 15 min total -# 13:40 -[https://www.youtube.com/watch?v=87uo2TPrsl8]
deleted file mode 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/short2-15.ini +++ /dev/null @@ -1,5 +0,0 @@ -# 1-2 longer videos with ads; < 15 minutes total -[https://www.youtube.com/watch?v=v678Em6qyzk] -[https://www.youtube.com/watch?v=l8XOZJkozfI] - -
rename from dom/media/test/external/external_media_tests/urls/youtube/short3-crashes-15.ini rename to dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini --- a/dom/media/test/external/external_media_tests/urls/youtube/short3-crashes-15.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini @@ -1,8 +1,11 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + # crash-data videos, < 15 minutes total # hang | NtUserMessageCall | SendMessageW # 5:40 [https://www.youtube.com/watch?v=UIobdRNLNek] # F1398665248_____________________________ # 3:59
--- a/dom/plugins/base/npapi.h +++ b/dom/plugins/base/npapi.h @@ -41,16 +41,17 @@ #endif #endif #if defined(XP_UNIX) #include <stdio.h> #if defined(MOZ_X11) #include <X11/Xlib.h> #include <X11/Xutil.h> +#include "X11UndefineNone.h" #endif #endif #if defined(XP_SYMBIAN) #include <QEvent> #include <QRegion> #endif
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -2605,17 +2605,17 @@ nsEventStatus nsPluginInstanceOwner::Pro // Get reference point relative to screen: LayoutDeviceIntPoint rootPoint(-1, -1); if (widget) { rootPoint = anEvent.mRefPoint + widget->WidgetToScreenOffset(); } #ifdef MOZ_WIDGET_GTK Window root = GDK_ROOT_WINDOW(); #else - Window root = None; // Could XQueryTree, but this is not important. + Window root = X11None; // Could XQueryTree, but this is not important. #endif switch (anEvent.mMessage) { case eMouseOver: case eMouseOut: { XCrossingEvent& event = pluginEvent.xcrossing; event.type = anEvent.mMessage == eMouseOver ? @@ -2623,17 +2623,17 @@ nsEventStatus nsPluginInstanceOwner::Pro event.root = root; event.time = anEvent.mTime; event.x = pluginPoint.x; event.y = pluginPoint.y; event.x_root = rootPoint.x; event.y_root = rootPoint.y; event.state = XInputEventState(mouseEvent); // information lost - event.subwindow = None; + event.subwindow = X11None; event.mode = -1; event.detail = NotifyDetailNone; event.same_screen = True; event.focus = mContentFocused; } break; case eMouseMove: { @@ -2642,17 +2642,17 @@ nsEventStatus nsPluginInstanceOwner::Pro event.root = root; event.time = anEvent.mTime; event.x = pluginPoint.x; event.y = pluginPoint.y; event.x_root = rootPoint.x; event.y_root = rootPoint.y; event.state = XInputEventState(mouseEvent); // information lost - event.subwindow = None; + event.subwindow = X11None; event.is_hint = NotifyNormal; event.same_screen = True; } break; case eMouseDown: case eMouseUp: { XButtonEvent& event = pluginEvent.xbutton; @@ -2673,17 +2673,17 @@ nsEventStatus nsPluginInstanceOwner::Pro case WidgetMouseEvent::eRightButton: event.button = 3; break; default: // WidgetMouseEvent::eLeftButton; event.button = 1; break; } // information lost: - event.subwindow = None; + event.subwindow = X11None; event.same_screen = True; } break; default: break; } } break; @@ -2717,17 +2717,17 @@ nsEventStatus nsPluginInstanceOwner::Pro break; default: break; } #endif // Information that could be obtained from pluginEvent but we may not // want to promise to provide: - event.subwindow = None; + event.subwindow = X11None; event.x = 0; event.y = 0; event.x_root = -1; event.y_root = -1; event.same_screen = False; } else { @@ -2759,17 +2759,17 @@ nsEventStatus nsPluginInstanceOwner::Pro if (!pluginEvent.type) { return rv; } // Fill in (useless) generic event information. XAnyEvent& event = pluginEvent.xany; event.display = widget ? static_cast<Display*>(widget->GetNativeData(NS_NATIVE_DISPLAY)) : nullptr; - event.window = None; // not a real window + event.window = X11None; // not a real window // information lost: event.serial = 0; event.send_event = False; int16_t response = kNPEventNotHandled; mInstance->HandleEvent(&pluginEvent, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); if (response == kNPEventHandled) rv = nsEventStatus_eConsumeNoDefault;
--- a/dom/plugins/base/nsPluginNativeWindowGtk.cpp +++ b/dom/plugins/base/nsPluginNativeWindowGtk.cpp @@ -212,17 +212,17 @@ nsresult nsPluginNativeWindowGtk::Create return NS_ERROR_FAILURE; mWsInfo.display = GDK_WINDOW_XDISPLAY(gdkWindow); #if (MOZ_WIDGET_GTK == 2) mWsInfo.colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(gdkWindow)); GdkVisual* gdkVisual = gdk_drawable_get_visual(gdkWindow); mWsInfo.depth = gdkVisual->depth; #else - mWsInfo.colormap = None; + mWsInfo.colormap = X11None; GdkVisual* gdkVisual = gdk_window_get_visual(gdkWindow); mWsInfo.depth = gdk_visual_get_depth(gdkVisual); #endif mWsInfo.visual = GDK_VISUAL_XVISUAL(gdkVisual); return NS_OK; }
--- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -1277,17 +1277,17 @@ PluginInstanceChild::AnswerNPP_SetWindow // workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061 // See wrap_gtk_plug_embedded in PluginModuleChild.cpp. g_object_set_data(G_OBJECT(socket_window), "moz-existed-before-set-window", GUINT_TO_POINTER(1)); } } - if (aWindow.visualID != None + if (aWindow.visualID != X11None && gtk_check_version(2, 12, 10) != nullptr) { // older // Workaround for a bug in Gtk+ (prior to 2.12.10) where deleting // a foreign GdkColormap will also free the XColormap. // http://git.gnome.org/browse/gtk+/log/gdk/x11/gdkcolor-x11.c?id=GTK_2_12_10 GdkVisual *gdkvisual = gdkx_visual_get(aWindow.visualID); GdkColormap *gdkcolor = gdk_x11_colormap_foreign_new(gdkvisual, aWindow.colormap);
--- a/dom/plugins/test/testplugin/nptest_gtk2.cpp +++ b/dom/plugins/test/testplugin/nptest_gtk2.cpp @@ -76,33 +76,33 @@ pluginInstanceInit(InstanceData* instanc #ifdef MOZ_X11 instanceData->platformData = static_cast<PlatformData*> (NPN_MemAlloc(sizeof(PlatformData))); if (!instanceData->platformData) return NPERR_OUT_OF_MEMORY_ERROR; instanceData->platformData->display = nullptr; instanceData->platformData->visual = nullptr; - instanceData->platformData->colormap = None; + instanceData->platformData->colormap = X11None; instanceData->platformData->plug = nullptr; return NPERR_NO_ERROR; #else // we only support X11 here, since thats what the plugin system uses return NPERR_INCOMPATIBLE_VERSION_ERROR; #endif } void pluginInstanceShutdown(InstanceData* instanceData) { if (instanceData->hasWidget) { Window window = reinterpret_cast<XID>(instanceData->window.window); - if (window != None) { + if (window != X11None) { // This window XID should still be valid. // See bug 429604 and bug 454756. XWindowAttributes attributes; if (!XGetWindowAttributes(instanceData->platformData->display, window, &attributes)) g_error("XGetWindowAttributes failed at plugin instance shutdown"); } }
--- a/gfx/2d/BorrowedContext.h +++ b/gfx/2d/BorrowedContext.h @@ -6,16 +6,17 @@ #ifndef _MOZILLA_GFX_BORROWED_CONTEXT_H #define _MOZILLA_GFX_BORROWED_CONTEXT_H #include "2D.h" #ifdef MOZ_X11 #include <X11/extensions/Xrender.h> #include <X11/Xlib.h> +#include "X11UndefineNone.h" #endif struct _cairo; typedef struct _cairo cairo_t; namespace mozilla { namespace gfx { @@ -82,26 +83,26 @@ private: * to see if it succeeded. The DrawTarget should not be used while * the drawable is borrowed. */ class BorrowedXlibDrawable { public: BorrowedXlibDrawable() : mDT(nullptr), mDisplay(nullptr), - mDrawable(None), + mDrawable(X11None), mScreen(nullptr), mVisual(nullptr), mXRenderFormat(nullptr) {} explicit BorrowedXlibDrawable(DrawTarget *aDT) : mDT(nullptr), mDisplay(nullptr), - mDrawable(None), + mDrawable(X11None), mScreen(nullptr), mVisual(nullptr), mXRenderFormat(nullptr) { Init(aDT); } // We can optionally Init after construction in
--- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -2158,17 +2158,17 @@ DrawTargetCairo::Draw3DTransformedSurfac XRenderSetPictureTransform(display, srcPict, (XTransform*)&xform); Picture dstPict = XRenderCreatePicture(display, cairo_xlib_surface_get_drawable(xformSurf), cairo_xlib_surface_get_xrender_format(xformSurf), 0, nullptr); XRenderComposite(display, PictOpSrc, - srcPict, None, dstPict, + srcPict, X11None, dstPict, 0, 0, 0, 0, 0, 0, xformBounds.width, xformBounds.height); XRenderFreePicture(display, srcPict); XRenderFreePicture(display, dstPict); cairo_device_release(device); cairo_surface_mark_dirty(xformSurf); @@ -2308,17 +2308,17 @@ BorrowedCairoContext::ReturnCairoContext #ifdef MOZ_X11 bool BorrowedXlibDrawable::Init(DrawTarget* aDT) { MOZ_ASSERT(aDT, "Caller should check for nullptr"); MOZ_ASSERT(!mDT, "Can't initialize twice!"); mDT = aDT; - mDrawable = None; + mDrawable = X11None; #ifdef CAIRO_HAS_XLIB_SURFACE if (aDT->GetBackendType() != BackendType::CAIRO || aDT->IsDualDrawTarget() || aDT->IsTiledDrawTarget()) { return false; } @@ -2351,15 +2351,15 @@ BorrowedXlibDrawable::Init(DrawTarget* a void BorrowedXlibDrawable::Finish() { DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(mDT); cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext); cairo_surface_mark_dirty(surf); if (mDrawable) { - mDrawable = None; + mDrawable = X11None; } } #endif } // namespace gfx } // namespace mozilla
--- a/gfx/gl/GLContextProviderGLX.cpp +++ b/gfx/gl/GLContextProviderGLX.cpp @@ -6,16 +6,17 @@ #ifdef MOZ_WIDGET_GTK #include <gdk/gdk.h> #include <gdk/gdkx.h> #define GET_NATIVE_WINDOW(aWidget) GDK_WINDOW_XID((GdkWindow*) aWidget->GetNativeData(NS_NATIVE_WINDOW)) #endif #include <X11/Xlib.h> #include <X11/Xutil.h> +#include "X11UndefineNone.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/StaticPtr.h" #include "mozilla/widget/CompositorWidget.h" #include "mozilla/Unused.h" #include "prenv.h" #include "GLContextProvider.h" @@ -303,36 +304,36 @@ GLXLibrary::SupportsVideoSync() return mHasVideoSync; } GLXPixmap GLXLibrary::CreatePixmap(gfxASurface* aSurface) { if (!SupportsTextureFromPixmap(aSurface)) { - return None; + return X11None; } gfxXlibSurface* xs = static_cast<gfxXlibSurface*>(aSurface); const XRenderPictFormat* format = xs->XRenderFormat(); if (!format || format->type != PictTypeDirect) { - return None; + return X11None; } const XRenderDirectFormat& direct = format->direct; int alphaSize = FloorLog2(direct.alphaMask + 1); NS_ASSERTION((1 << alphaSize) - 1 == direct.alphaMask, "Unexpected render format with non-adjacent alpha bits"); int attribs[] = { LOCAL_GLX_DOUBLEBUFFER, False, LOCAL_GLX_DRAWABLE_TYPE, LOCAL_GLX_PIXMAP_BIT, LOCAL_GLX_ALPHA_SIZE, alphaSize, (alphaSize ? LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT : LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT), True, LOCAL_GLX_RENDER_TYPE, LOCAL_GLX_RGBA_BIT, - None }; + X11None }; int numConfigs = 0; Display* display = xs->XDisplay(); int xscreen = DefaultScreen(display); ScopedXFree<GLXFBConfig> cfgs(xChooseFBConfig(display, xscreen, attribs, @@ -346,17 +347,17 @@ GLXLibrary::CreatePixmap(gfxASurface* aS static_cast<unsigned long>(direct.greenMask) << direct.green; unsigned long blueMask = static_cast<unsigned long>(direct.blueMask) << direct.blue; // This is true if the Pixmap has bits for alpha or unused bits. bool haveNonColorBits = ~(redMask | greenMask | blueMask) != -1UL << format->depth; for (int i = 0; i < numConfigs; i++) { - int id = None; + int id = X11None; sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &id); Visual* visual; int depth; FindVisualAndDepth(display, id, &visual, &depth); if (!visual || visual->c_class != TrueColor || visual->red_mask != redMask || visual->green_mask != greenMask || @@ -419,24 +420,24 @@ GLXLibrary::CreatePixmap(gfxASurface* aS matchIndex = i; break; } if (matchIndex == -1) { // GLX can't handle A8 surfaces, so this is not really unexpected. The // caller should deal with this situation. NS_WARN_IF_FALSE(format->depth == 8, "[GLX] Couldn't find a FBConfig matching Pixmap format"); - return None; + return X11None; } int pixmapAttribs[] = { LOCAL_GLX_TEXTURE_TARGET_EXT, LOCAL_GLX_TEXTURE_2D_EXT, LOCAL_GLX_TEXTURE_FORMAT_EXT, (alphaSize ? LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT : LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT), - None}; + X11None}; GLXPixmap glxpixmap = xCreatePixmap(display, cfgs[matchIndex], xs->XDrawable(), pixmapAttribs); return glxpixmap; } @@ -895,17 +896,17 @@ GLContextGLX::~GLContextGLX() if (!mOwnsContext) { return; } // see bug 659842 comment 76 #ifdef DEBUG bool success = #endif - mGLX->xMakeCurrent(mDisplay, None, nullptr); + mGLX->xMakeCurrent(mDisplay, X11None, nullptr); MOZ_ASSERT(success, "glXMakeCurrent failed to release GL context before we call " "glXDestroyContext!"); mGLX->xDestroyContext(mDisplay, mContext); if (mDeleteDrawable) { mGLX->xDestroyPixmap(mDisplay, mDrawable); @@ -1237,17 +1238,17 @@ GLContextGLX::FindFBConfigForWindow(Disp return false; } const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual); #ifdef DEBUG printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID); #endif for (int i = 0; i < numConfigs; i++) { - int visid = None; + int visid = X11None; sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid); if (!visid) { continue; } if (sGLXLibrary.IsATI()) { int depth; Visual* visual; FindVisualAndDepth(display, visid, &visual, &depth);
--- a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp @@ -6,16 +6,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ShadowLayerUtilsX11.h" #include <X11/X.h> // for Drawable, XID #include <X11/Xlib.h> // for Display, Visual, etc #include <X11/extensions/Xrender.h> // for XRenderPictFormat, etc #include <X11/extensions/render.h> // for PictFormat #include "cairo-xlib.h" +#include "X11UndefineNone.h" #include <stdint.h> // for uint32_t #include "GLDefs.h" // for GLenum #include "gfxPlatform.h" // for gfxPlatform #include "gfxXlibSurface.h" // for gfxXlibSurface #include "gfx2DGlue.h" // for Moz2D transistion helpers #include "mozilla/X11Util.h" // for DefaultXDisplay, FinishX, etc #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/CompositableForwarder.h" @@ -60,17 +61,17 @@ GetXRenderPictFormatFromId(Display* aDis tmplate.id = aFormatId; return XRenderFindFormat(aDisplay, PictFormatID, &tmplate, 0); } SurfaceDescriptorX11::SurfaceDescriptorX11(gfxXlibSurface* aSurf, bool aForwardGLX) : mId(aSurf->XDrawable()) , mSize(aSurf->GetSize()) - , mGLXPixmap(None) + , mGLXPixmap(X11None) { const XRenderPictFormat *pictFormat = aSurf->XRenderFormat(); if (pictFormat) { mFormat = pictFormat->id; } else { mFormat = cairo_xlib_surface_get_visual(aSurf->CairoSurface())->visualid; } @@ -81,17 +82,17 @@ SurfaceDescriptorX11::SurfaceDescriptorX #endif } SurfaceDescriptorX11::SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID, const gfx::IntSize& aSize) : mId(aDrawable) , mFormat(aFormatID) , mSize(aSize) - , mGLXPixmap(None) + , mGLXPixmap(X11None) { } already_AddRefed<gfxXlibSurface> SurfaceDescriptorX11::OpenForeign() const { Display* display = DefaultXDisplay(); Screen* screen = DefaultScreenOfDisplay(display);
new file mode 100644 --- /dev/null +++ b/gfx/src/X11UndefineNone.h @@ -0,0 +1,27 @@ +/* -*- 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 MOZILLA_GFX_X11UNDEFINENONE_H_ +#define MOZILLA_GFX_X11UNDEFINENONE_H_ + +// The header <X11/X.h> defines "None" as a macro that expands to "0L". +// This is terrible because many enumerations have an enumerator named "None". +// To work around this, we undefine the macro "None", and define a replacement +// macro named "X11None". +// Include this header after including X11 headers, where necessary. +#ifdef None +# undef None +# define X11None 0L +// <X11/X.h> also defines "RevertToNone" as a macro that expands to "(int)None". +// Since we are undefining "None", that stops working. To keep it working, +// we undefine "RevertToNone" and redefine it in terms of "X11None". +# ifdef RevertToNone +# undef RevertToNone +# define RevertToNone (int)X11None +# endif +#endif + +#endif /* MOZILLA_GFX_X11UNDEFINENONE_H_ */
--- a/gfx/src/X11Util.cpp +++ b/gfx/src/X11Util.cpp @@ -24,17 +24,17 @@ FindVisualAndDepth(Display* aDisplay, Vi if (visual->visualid == aVisualID) { *aVisual = visual; *aDepth = d_info->depth; return; } } } - NS_ASSERTION(aVisualID == None, "VisualID not on Screen."); + NS_ASSERTION(aVisualID == X11None, "VisualID not on Screen."); *aVisual = nullptr; *aDepth = 0; return; } void FinishX(Display* aDisplay) {
--- a/gfx/src/X11Util.h +++ b/gfx/src/X11Util.h @@ -8,16 +8,17 @@ #ifndef mozilla_X11Util_h #define mozilla_X11Util_h // Utilities common to all X clients, regardless of UI toolkit. #if defined(MOZ_WIDGET_GTK) # include <gdk/gdk.h> # include <gdk/gdkx.h> +# include "X11UndefineNone.h" #else # error Unknown toolkit #endif #include <string.h> // for memset #include "mozilla/Scoped.h" // for SCOPED_TEMPLATE namespace mozilla {
--- a/gfx/src/moz.build +++ b/gfx/src/moz.build @@ -34,16 +34,17 @@ EXPORTS += [ 'nsRegion.h', 'nsRegionFwd.h', 'nsRenderingContext.h', 'nsSize.h', 'nsThemeConstants.h', 'nsTransform2D.h', 'PingPongRegion.h', 'RegionBuilder.h', + 'X11UndefineNone.h' ] EXPORTS.mozilla += [ 'AppUnits.h', 'ArrayView.h', ] EXPORTS.mozilla.gfx += [
--- a/gfx/thebes/gfxXlibSurface.cpp +++ b/gfx/thebes/gfxXlibSurface.cpp @@ -20,59 +20,59 @@ #include "mozilla/CheckedInt.h" using namespace mozilla; using namespace mozilla::gfx; gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual) : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { const gfx::IntSize size = DoSizeQuery(); cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height); Init(surf); } gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const gfx::IntSize& size) : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT), "Bad size"); cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height); Init(surf); } gfxXlibSurface::gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFormat *format, const gfx::IntSize& size) : mPixmapTaken(false), mDisplay(DisplayOfScreen(screen)), mDrawable(drawable) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT), "Bad Size"); cairo_surface_t *surf = cairo_xlib_surface_create_with_xrender_format(mDisplay, drawable, screen, format, size.width, size.height); Init(surf); } gfxXlibSurface::gfxXlibSurface(cairo_surface_t *csurf) : mPixmapTaken(false) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { NS_PRECONDITION(cairo_surface_status(csurf) == 0, "Not expecting an error surface"); mDrawable = cairo_xlib_surface_get_drawable(csurf); mDisplay = cairo_xlib_surface_get_display(csurf); @@ -92,19 +92,19 @@ gfxXlibSurface::~gfxXlibSurface() } } static Drawable CreatePixmap(Screen *screen, const gfx::IntSize& size, unsigned int depth, Drawable relatedDrawable) { if (!Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT)) - return None; + return X11None; - if (relatedDrawable == None) { + if (relatedDrawable == X11None) { relatedDrawable = RootWindowOfScreen(screen); } Display *dpy = DisplayOfScreen(screen); // X gives us a fatal error if we try to create a pixmap of width // or height 0 return XCreatePixmap(dpy, relatedDrawable, std::max(1, size.width), std::max(1, size.height), depth); @@ -269,17 +269,17 @@ gfxXlibSurface::CreateSimilarSurface(gfx } void gfxXlibSurface::Finish() { #if defined(GL_PROVIDER_GLX) if (mPixmapTaken && mGLXPixmap) { gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap); - mGLXPixmap = None; + mGLXPixmap = X11None; } #endif gfxASurface::Finish(); } const gfx::IntSize gfxXlibSurface::GetSize() const {
--- a/gfx/thebes/gfxXlibSurface.h +++ b/gfx/thebes/gfxXlibSurface.h @@ -5,16 +5,17 @@ #ifndef GFX_XLIBSURFACE_H #define GFX_XLIBSURFACE_H #include "gfxASurface.h" #include <X11/extensions/Xrender.h> #include <X11/Xlib.h> +#include "X11UndefineNone.h" #if defined(GL_PROVIDER_GLX) #include "GLXLibrary.h" #endif #include "nsSize.h" // Although the dimension parameters in the xCreatePixmapReq wire protocol are @@ -41,23 +42,23 @@ public: explicit gfxXlibSurface(cairo_surface_t *csurf); // create a new Pixmap and wrapper surface. // |relatedDrawable| provides a hint to the server for determining whether // the pixmap should be in video or system memory. It must be on // |screen| (if specified). static already_AddRefed<gfxXlibSurface> Create(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size, - Drawable relatedDrawable = None); + Drawable relatedDrawable = X11None); static cairo_surface_t * CreateCairoSurface(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size, - Drawable relatedDrawable = None); + Drawable relatedDrawable = X11None); static already_AddRefed<gfxXlibSurface> Create(Screen* screen, XRenderPictFormat *format, const mozilla::gfx::IntSize& size, - Drawable relatedDrawable = None); + Drawable relatedDrawable = X11None); virtual ~gfxXlibSurface(); virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType, const mozilla::gfx::IntSize& aSize) override; virtual void Finish() override;
--- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -477,17 +477,19 @@ ServoStyleSet::StyleDocument(bool aLeave doc->UnsetHasDirtyDescendantsForServo(); } } void ServoStyleSet::StyleNewSubtree(nsIContent* aContent) { MOZ_ASSERT(aContent->IsDirtyForServo()); - Servo_RestyleSubtree(aContent, mRawSet.get()); + if (aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)) { + Servo_RestyleSubtree(aContent, mRawSet.get()); + } ClearDirtyBits(aContent); } void ServoStyleSet::StyleNewChildren(nsIContent* aParent) { MOZ_ASSERT(aParent->HasDirtyDescendantsForServo()); Servo_RestyleSubtree(aParent, mRawSet.get());
--- a/media/mtransport/test/ice_unittest.cpp +++ b/media/mtransport/test/ice_unittest.cpp @@ -758,17 +758,17 @@ class IceTestPeer : public sigslot::has_ RefPtr<NrIceMediaStream> aStream = ice_ctx_->ctx()->GetStream(i); if (!aStream || aStream->HasParsedAttributes()) { continue; } std::vector<std::string> candidates = remote->GetCandidates(i); for (size_t j=0; j<candidates.size(); ++j) { - std::cerr << name_ << " Candidate: " + candidates[j] << std::endl; + std::cerr << name_ << " Adding remote candidate: " + candidates[j] << std::endl; } res = aStream->ParseAttributes(candidates); ASSERT_TRUE(NS_SUCCEEDED(res)); } } else { // Parse empty attributes and then trickle them out later for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) { RefPtr<NrIceMediaStream> aStream = ice_ctx_->ctx()->GetStream(i);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c @@ -176,17 +176,18 @@ int nr_ice_candidate_pair_unfreeze(nr_ic nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING); return(0); } static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) { int r,_status; - nr_ice_cand_pair *pair=cb_arg,*orig_pair; + nr_ice_cand_pair *pair=cb_arg; + nr_ice_cand_pair *actual_pair=0; nr_ice_candidate *cand=0; nr_stun_message *sres; nr_transport_addr *request_src; nr_transport_addr *request_dst; nr_transport_addr *response_src; nr_transport_addr response_dst; nr_stun_message_attribute *attr; @@ -251,58 +252,65 @@ static void nr_ice_candidate_pair_stun_c nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED); } else if(pair->stun_client->state == NR_STUN_CLIENT_STATE_DONE) { /* OK, this didn't correspond to a pair on the check list, but it probably matches one of our candidates */ cand=TAILQ_FIRST(&pair->local->component->candidates); while(cand){ - if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): found pre-existing local candidate of type %d for mapped address %s", pair->pctx->label,cand->type,cand->addr.as_string); + assert(cand->type != HOST); break; + } cand=TAILQ_NEXT(cand,entry_comp); } - /* OK, nothing found, must be peer reflexive */ if(!cand) { + /* OK, nothing found, must be a new peer reflexive */ if (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) { /* Any STUN response with a reflexive address in it is unwanted when we'll send on relay only. Bail since cand is used below. */ goto done; } if(r=nr_ice_candidate_create(pair->pctx->ctx, pair->local->component,pair->local->isock,pair->local->osock, PEER_REFLEXIVE,pair->local->tcp_type,0,pair->local->component->component_id,&cand)) ABORT(r); if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr)) ABORT(r); cand->state=NR_ICE_CAND_STATE_INITIALIZED; TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp); + } else { + /* Check if we have a pair for this candidate already. */ + if(r=nr_ice_media_stream_find_pair(pair->remote->stream, cand, pair->remote, &actual_pair)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no pair exists for %s and %s", pair->pctx->label,cand->addr.as_string, pair->remote->addr.as_string); + } } - /* Note: we stomp the existing pair! */ - orig_pair=pair; - if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, - &pair)) - ABORT(r); + if(!actual_pair) { + if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &actual_pair)) + ABORT(r); - nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED); + if(r=nr_ice_component_insert_pair(actual_pair->remote->component,actual_pair)) + ABORT(r); - if(r=nr_ice_component_insert_pair(pair->remote->component,pair)) - ABORT(r); + /* If the original pair was nominated, make us nominated too. */ + if(pair->peer_nominated) + actual_pair->peer_nominated=1; - /* If the original pair was nominated, make us nominated, - since we replace him*/ - if(orig_pair->peer_nominated) - pair->peer_nominated=1; + /* Now mark the orig pair failed */ + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + } - - /* Now mark the orig pair failed */ - nr_ice_candidate_pair_set_state(orig_pair->pctx,orig_pair,NR_ICE_PAIR_STATE_FAILED); + assert(actual_pair); + nr_ice_candidate_pair_set_state(actual_pair->pctx,actual_pair,NR_ICE_PAIR_STATE_SUCCEEDED); + pair=actual_pair; } /* Should we set nominated? */ if(pair->pctx->controlling){ if(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION) pair->nominated=1; }
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c @@ -908,8 +908,26 @@ void nr_ice_media_stream_role_change(nr_ /* Re-insert into the check list */ TAILQ_FOREACH_SAFE(pair,&old_checklist,check_queue_entry,temp_pair) { TAILQ_REMOVE(&old_checklist,pair,check_queue_entry); nr_ice_candidate_pair_role_change(pair); nr_ice_candidate_pair_insert(&stream->check_list,pair); } } +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *lcand, nr_ice_candidate *rcand, nr_ice_cand_pair **pair) + { + nr_ice_cand_pair_head *head = &str->check_list; + nr_ice_cand_pair *c1; + + c1=TAILQ_FIRST(head); + while(c1){ + if(c1->local == lcand && + c1->remote == rcand) { + *pair=c1; + return(0); + } + + c1=TAILQ_NEXT(c1,check_queue_entry); + } + + return(R_NOT_FOUND); + }
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h +++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h @@ -87,16 +87,17 @@ int nr_ice_media_stream_unfreeze_pairs_f int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out); int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component); int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component); int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp); int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len); int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote); int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp); +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *local, nr_ice_candidate *remote, nr_ice_cand_pair **pair); int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote); int nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr); int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int component_id, int *can_send, struct timeval *ts); int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id); int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand); void nr_ice_media_stream_role_change(nr_ice_media_stream *stream);
--- a/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java +++ b/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java @@ -7,17 +7,16 @@ package org.mozilla.gecko; import org.mozilla.gecko.overlays.ui.ShareDialog; import org.mozilla.gecko.util.ActivityResultHandler; import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.JSONUtils; import org.mozilla.gecko.util.NativeEventListener; import org.mozilla.gecko.util.NativeJSObject; -import org.mozilla.gecko.util.WebActivityMapper; import org.mozilla.gecko.widget.ExternalIntentDuringPrivateBrowsingPromptFragment; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.annotation.TargetApi; import android.content.Context; @@ -43,17 +42,16 @@ import java.util.Locale; public final class IntentHelper implements GeckoEventListener, NativeEventListener { private static final String LOGTAG = "GeckoIntentHelper"; private static final String[] EVENTS = { "Intent:GetHandlers", "Intent:Open", "Intent:OpenForResult", - "WebActivity:Open" }; private static final String[] NATIVE_EVENTS = { "Intent:OpenNoHandler", }; // via http://developer.android.com/distribute/tools/promote/linking.html private static String MARKET_INTENT_URI_PACKAGE_PREFIX = "market://details?id="; @@ -411,18 +409,16 @@ public final class IntentHelper implemen public void handleMessage(String event, JSONObject message) { try { if (event.equals("Intent:GetHandlers")) { getHandlers(message); } else if (event.equals("Intent:Open")) { open(message); } else if (event.equals("Intent:OpenForResult")) { openForResult(message); - } else if (event.equals("WebActivity:Open")) { - openWebActivity(message); } } catch (JSONException e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); } } private void getHandlers(JSONObject message) throws JSONException { final Intent intent = getOpenURIIntent(activity, @@ -567,21 +563,16 @@ public final class IntentHelper implemen * @param encodedUri The encoded uri. While the page does not open correctly without specifying * a uri parameter, it happily accepts the empty String so this argument may * be the empty String. */ private String getUnknownProtocolErrorPageUri(final String encodedUri) { return UNKNOWN_PROTOCOL_URI_PREFIX + encodedUri; } - private void openWebActivity(JSONObject message) throws JSONException { - final Intent intent = WebActivityMapper.getIntentForWebActivity(message.getJSONObject("activity")); - ActivityHandlerHelper.startIntentForActivity(activity, intent, new ResultHandler(message)); - } - private static class ResultHandler implements ActivityResultHandler { private final JSONObject message; public ResultHandler(JSONObject message) { this.message = message; } @Override
--- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -134,17 +134,16 @@ gujar.sources += [geckoview_source_dir + 'util/PrefUtils.java', 'util/ProxySelector.java', 'util/RawResource.java', 'util/StringUtils.java', 'util/ThreadUtils.java', 'util/UIAsyncTask.java', 'util/UUIDUtil.java', 'util/WeakReferenceHandler.java', - 'util/WebActivityMapper.java', 'util/WindowUtils.java', ]] gujar.extra_jars = [ CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'], CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'], CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'], 'constants.jar', 'gecko-mozglue.jar',
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -52,20 +52,18 @@ XPCOMUtils.defineLazyModuleGetter(this, "resource://gre/modules/LoginManagerContent.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -if (AppConstants.MOZ_SAFE_BROWSING) { - XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", - "resource://gre/modules/SafeBrowsing.jsm"); -} +XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", + "resource://gre/modules/SafeBrowsing.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer", @@ -544,20 +542,18 @@ var BrowserApp = { }); InitLater(() => LightWeightThemeWebInstaller.init()); InitLater(() => SpatialNavigation.init(BrowserApp.deck, null), window, "SpatialNavigation"); InitLater(() => CastingApps.init(), window, "CastingApps"); InitLater(() => Services.search.init(), Services, "search"); InitLater(() => DownloadNotifications.init(), window, "DownloadNotifications"); - if (AppConstants.MOZ_SAFE_BROWSING) { - // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008. - InitLater(() => SafeBrowsing.init(), window, "SafeBrowsing"); - } + // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008. + InitLater(() => SafeBrowsing.init(), window, "SafeBrowsing"); InitLater(() => Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager)); InitLater(() => LoginManagerParent.init(), window, "LoginManagerParent"); }, false); // Pass caret StateChanged events to ActionBarHandler. window.addEventListener("mozcaretstatechanged", e => {
deleted file mode 100644 --- a/mobile/android/components/AndroidActivitiesGlue.js +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict" - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -function ActivitiesGlue() { } - -ActivitiesGlue.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIActivityUIGlue]), - classID: Components.ID("{e4deb5f6-d5e3-4fce-bc53-901dd9951c48}"), - - // Ignore aActivities results on Android, go straight to Android intents. - chooseActivity: function ap_chooseActivity(aOptions, aActivities, aCallback) { - Messaging.sendRequestForResult({ - type: "WebActivity:Open", - activity: { name: aOptions.name, data: aOptions.data } - }).then((result) => { - aCallback.handleEvent(Ci.nsIActivityUIGlueCallback.NATIVE_ACTIVITY, result); - }); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ActivitiesGlue]);
--- a/mobile/android/components/LoginManagerPrompter.js +++ b/mobile/android/components/LoginManagerPrompter.js @@ -100,28 +100,33 @@ LoginManagerPrompter.prototype = { /* ---------- nsILoginManagerPrompter prompts ---------- */ /* * init * */ init : function (aWindow, aFactory) { - this._window = aWindow; + this._chromeWindow = this._getChromeWindow(aWindow).wrappedJSObject; this._factory = aFactory || null; + this._browser = null; var prefBranch = Services.prefs.getBranch("signon."); this._debug = prefBranch.getBoolPref("debug"); this.log("===== initialized ====="); }, - setE10sData : function (aBrowser, aOpener) { - throw new Error("This should be filled in when Android is multiprocess"); + set browser(aBrowser) { + this._browser = aBrowser; }, + // setting this attribute is ignored because Android does not consider + // opener windows when displaying login notifications + set opener(aOpener) { }, + /* * promptToSavePassword * */ promptToSavePassword : function (aLogin) { this._showSaveLoginNotification(aLogin); Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION").add(PROMPT_DISPLAYED); }, @@ -135,20 +140,17 @@ LoginManagerPrompter.prototype = { * @param aButtons * Buttons to display with the doorhanger * @param aUsername * Username string used in creating a doorhanger action * @param aPassword * Password string used in creating a doorhanger action */ _showLoginNotification : function (aBody, aButtons, aUsername, aPassword) { - let notifyWin = this._window.top; - let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; - let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin); - let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id; + let tabID = this._chromeWindow.BrowserApp.getTabForBrowser(this._browser).id; let actionText = { text: aUsername, type: "EDIT", bundle: { username: aUsername, password: aPassword } }; @@ -329,34 +331,35 @@ LoginManagerPrompter.prototype = { }, /* * _getChromeWindow * * Given a content DOM window, returns the chrome window it's in. */ _getChromeWindow: function (aWindow) { + if (aWindow instanceof Ci.nsIDOMChromeWindow) + return aWindow; var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell) .chromeEventHandler.ownerDocument.defaultView; return chromeWin; }, /* * _getNativeWindow * * Returns the NativeWindow to this prompter, or null if there isn't * a NativeWindow available (w/ error sent to logcat). */ _getNativeWindow : function () { let nativeWindow = null; try { - let notifyWin = this._window.top; - let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; + let chromeWin = this._chromeWindow; if (chromeWin.NativeWindow) { nativeWindow = chromeWin.NativeWindow; } else { Cu.reportError("NativeWindow not available on window"); } } catch (e) { // If any errors happen, just assume no native window helper.
--- a/mobile/android/components/MobileComponents.manifest +++ b/mobile/android/components/MobileComponents.manifest @@ -9,19 +9,17 @@ contract @mozilla.org/network/protocol/a contract @mozilla.org/network/protocol/about;1?what=home {322ba47e-7047-4f71-aebf-cb7d69325cd9} contract @mozilla.org/network/protocol/about;1?what=downloads {322ba47e-7047-4f71-aebf-cb7d69325cd9} contract @mozilla.org/network/protocol/about;1?what=reader {322ba47e-7047-4f71-aebf-cb7d69325cd9} contract @mozilla.org/network/protocol/about;1?what=feedback {322ba47e-7047-4f71-aebf-cb7d69325cd9} contract @mozilla.org/network/protocol/about;1?what=privatebrowsing {322ba47e-7047-4f71-aebf-cb7d69325cd9} #ifdef MOZ_SERVICES_HEALTHREPORT contract @mozilla.org/network/protocol/about;1?what=healthreport {322ba47e-7047-4f71-aebf-cb7d69325cd9} #endif -#ifdef MOZ_SAFE_BROWSING contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9} -#endif contract @mozilla.org/network/protocol/about;1?what=accounts {322ba47e-7047-4f71-aebf-cb7d69325cd9} contract @mozilla.org/network/protocol/about;1?what=logins {322ba47e-7047-4f71-aebf-cb7d69325cd9} # DirectoryProvider.js component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1 @@ -112,17 +110,13 @@ component {a78d7e59-b558-4321-a3d6-dffe2 contract @mozilla.org/snippets;1 {a78d7e59-b558-4321-a3d6-dffe2f1e76dd} category browser-delayed-startup-finished Snippets @mozilla.org/snippets;1 category update-timer Snippets @mozilla.org/snippets;1,getService,snippets-update-timer,browser.snippets.updateInterval,86400 # ColorPicker.js component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js contract @mozilla.org/colorpicker;1 {430b987f-bb9f-46a3-99a5-241749220b29} -# AndroidActivitiesGlue.js -component {e4deb5f6-d5e3-4fce-bc53-901dd9951c48} AndroidActivitiesGlue.js -contract @mozilla.org/dom/activities/ui-glue;1 {e4deb5f6-d5e3-4fce-bc53-901dd9951c48} - # PersistentNotificationHandler.js component {75390fe7-f8a3-423a-b3b1-258d7eabed40} PersistentNotificationHandler.js contract @mozilla.org/persistent-notification-handler;1 {75390fe7-f8a3-423a-b3b1-258d7eabed40} category persistent-notification-click PersistentNotificationHandler @mozilla.org/persistent-notification-handler;1 category persistent-notification-close PersistentNotificationHandler @mozilla.org/persistent-notification-handler;1
--- a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html +++ b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html @@ -13,17 +13,17 @@ <script type="text/javascript"> "use strict"; let dataURI = "iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAC4klEQVRYhdWXLWzbQBSADQtDAwsHC1tUhUxqfL67lk2tdn+OJg0ODU0rLByqgqINBY6tmlbn7LMTJ5FaFVVBk1G0oUGjG2jT2Y7jxmmcbU/6iJ+f36fz+e5sGP9riCGm9hB37RG+scd4Yo/wsDXCZyIE2xuXsce4bY+wXkAsQtzYmExrfFgvkJkRbkzo1ehoxx5iXcgI/9iYUGt8WH9MqDXEcmNChmEYrRCf2SHWeYgQx3x0tLNRIeKQLTtEFyJEep4NTuhk8BC+yMrwEE3+iozo42d8gK7FAOkMsRiiN8QhW2ttSK5QTfRRV4QoymVeJMvPvDp7gCZigD613MN6yRFA3SWarow9QB9LCfG+NeF9qCtjAKOSQjCqVKhfVsiHEQ+grgx/lRGqUihAc1uL8EFD+KCRO+GrF4J61phcoRoPoEzkYhZYpykh5sMb7kOdIeY+jHKur4QI4Feh4AFX1nVeLxrAvQchGsBz5ls6wa2QdwcvIcE2863bTH79KOvsz/uUYJsp+J0pSzNlDckVqqVGUAF+n6uS7txcOl6wot4JVy70ufDLy4pWLUQVPE81pRI0mGe9oxLMHSeohHvMs/STUNaUK6vDPCvOyxMFDx4achehRDJmHnydnkPww5OFfLxrGIZBFDyYl4LpMzlTQFIP6AQx86w2UeYBccFpJrcKv5L9eGDtUAU6RIELqsB74uynjy/UBRF1gS5BTFxwQT1wTiXoUg9MH7m/3NZRRoi5IJytUbMgzv4Wc832+oQkiKgEehmyMkkpKsFkQV11QsRJL5rJYBLItQgRaUZEmnoZXsomz3vGiWw+I9KMF9SVFOqZEemZekli1jN3U/UOqhHHvC6oWWGElhfSpGdOk6+O9prdwvtLj5BjRsQxdRnot+Zeifpy/2/0stktKTRNLmbk0mwXyl8253fyojj+8rxOHNAhjjm5n0/5OOCGOKBzkrMO0Z75lvSAzKlrF32Z/3z8BqLAn+yMV7VhAAAAAElFTkSuQmCC"; let image = atob(dataURI); const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; -function backgroundScript() { +function background() { browser.test.assertTrue("pageAction" in browser, "Namespace 'pageAction' exists in browser"); browser.test.assertTrue("show" in browser.pageAction, "API method 'show' exists in browser.pageAction"); // TODO: Use the Tabs API to obtain the tab ids for showing pageActions. let tabId = 1; browser.test.onMessage.addListener(msg => { if (msg === "pageAction-show") { browser.pageAction.show(tabId).then(() => { @@ -41,17 +41,17 @@ function backgroundScript() { browser.test.sendMessage("page-action-clicked"); }); browser.test.sendMessage("ready"); } add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension({ - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "name": "PageAction Extension", "page_action": { "default_title": "Page Action", "default_icon": { "18": "extension.png", }, },
--- a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html +++ b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html @@ -16,17 +16,17 @@ Cu.import("resource://gre/modules/Services.jsm"); let dataURI = "iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAC4klEQVRYhdWXLWzbQBSADQtDAwsHC1tUhUxqfL67lk2tdn+OJg0ODU0rLByqgqINBY6tmlbn7LMTJ5FaFVVBk1G0oUGjG2jT2Y7jxmmcbU/6iJ+f36fz+e5sGP9riCGm9hB37RG+scd4Yo/wsDXCZyIE2xuXsce4bY+wXkAsQtzYmExrfFgvkJkRbkzo1ehoxx5iXcgI/9iYUGt8WH9MqDXEcmNChmEYrRCf2SHWeYgQx3x0tLNRIeKQLTtEFyJEep4NTuhk8BC+yMrwEE3+iozo42d8gK7FAOkMsRiiN8QhW2ttSK5QTfRRV4QoymVeJMvPvDp7gCZigD613MN6yRFA3SWarow9QB9LCfG+NeF9qCtjAKOSQjCqVKhfVsiHEQ+grgx/lRGqUihAc1uL8EFD+KCRO+GrF4J61phcoRoPoEzkYhZYpykh5sMb7kOdIeY+jHKur4QI4Feh4AFX1nVeLxrAvQchGsBz5ls6wa2QdwcvIcE2863bTH79KOvsz/uUYJsp+J0pSzNlDckVqqVGUAF+n6uS7txcOl6wot4JVy70ufDLy4pWLUQVPE81pRI0mGe9oxLMHSeohHvMs/STUNaUK6vDPCvOyxMFDx4achehRDJmHnydnkPww5OFfLxrGIZBFDyYl4LpMzlTQFIP6AQx86w2UeYBccFpJrcKv5L9eGDtUAU6RIELqsB74uynjy/UBRF1gS5BTFxwQT1wTiXoUg9MH7m/3NZRRoi5IJytUbMgzv4Wc832+oQkiKgEehmyMkkpKsFkQV11QsRJL5rJYBLItQgRaUZEmnoZXsomz3vGiWw+I9KMF9SVFOqZEemZekli1jN3U/UOqhHHvC6oWWGElhfSpGdOk6+O9prdwvtLj5BjRsQxdRnot+Zeifpy/2/0stktKTRNLmbk0mwXyl8253fyojj+8rxOHNAhjjm5n0/5OOCGOKBzkrMO0Z75lvSAzKlrF32Z/3z8BqLAn+yMV7VhAAAAAElFTkSuQmCC"; let image = atob(dataURI); const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; add_task(function* test_contentscript() { - function backgroundScript() { + function background() { // TODO: Use the Tabs API to obtain the tab ids for showing pageActions. let tabId = 1; let onClickedListenerEnabled = false; browser.test.onMessage.addListener((msg, details) => { if (msg === "page-action-show") { // TODO: switch to using .show(tabId).then(...) once bug 1270742 lands. browser.pageAction.show(tabId).then(() => { @@ -66,33 +66,33 @@ add_task(function* test_contentscript() if (details.location == location.href) { window.close(); } } }); } let extension = ExtensionTestUtils.loadExtension({ - background: `(${backgroundScript}())`, + background, manifest: { "name": "PageAction Extension", "page_action": { "default_title": "Page Action", "default_popup": "default.html", "default_icon": { "18": "extension.png", }, }, }, files: { - "default.html": `<html><head><meta charset="utf-8"><script src="popup.js"></${"script"}></head></html>`, + "default.html": `<html><head><meta charset="utf-8"><script src="popup.js"><\/script></head></html>`, "extension.png": IMAGE_ARRAYBUFFER, - "a.html": `<html><head><meta charset="utf-8"><script src="popup.js"></${"script"}></head></html>`, - "b.html": `<html><head><meta charset="utf-8"><script src="popup.js"></${"script"}></head></html>`, - "popup.js": `(${popupScript})()`, + "a.html": `<html><head><meta charset="utf-8"><script src="popup.js"><\/script></head></html>`, + "b.html": `<html><head><meta charset="utf-8"><script src="popup.js"><\/script></head></html>`, + "popup.js": popupScript, }, }); let tabClosedPromise = () => { return new Promise(resolve => { let chromeWin = Services.wm.getMostRecentWindow("navigator:browser"); let BrowserApp = chromeWin.BrowserApp;
--- a/mobile/android/components/moz.build +++ b/mobile/android/components/moz.build @@ -8,17 +8,16 @@ XPIDL_SOURCES += [ 'SessionStore.idl', ] XPIDL_MODULE = 'MobileComponents' EXTRA_COMPONENTS += [ 'AboutRedirector.js', 'AddonUpdateService.js', - 'AndroidActivitiesGlue.js', 'BlocklistPrompt.js', 'BrowserCLH.js', 'ColorPicker.js', 'ContentDispatchChooser.js', 'ContentPermissionPrompt.js', 'DirectoryProvider.js', 'FilePicker.js', 'FxAccountsPush.js',
--- a/mobile/android/confvars.sh +++ b/mobile/android/confvars.sh @@ -18,18 +18,16 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/a MOZ_ANDROID_MIN_SDK_VERSION=15 # There are several entry points into the Firefox application. These are the names of some of the classes that are # listed in the Android manifest. They are specified in here to avoid hard-coding them in source code files. MOZ_ANDROID_APPLICATION_CLASS=org.mozilla.gecko.GeckoApplication MOZ_ANDROID_BROWSER_INTENT_CLASS=org.mozilla.gecko.BrowserApp MOZ_ANDROID_SEARCH_INTENT_CLASS=org.mozilla.search.SearchActivity -MOZ_SAFE_BROWSING=1 - MOZ_NO_SMART_CARDS=1 MOZ_XULRUNNER= MOZ_CAPTURE=1 MOZ_RAW=1 # use custom widget for html:select
deleted file mode 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebActivityMapper.java +++ /dev/null @@ -1,221 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * 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/. */ - -package org.mozilla.gecko.util; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.content.Intent; -import android.net.Uri; -import android.os.Environment; -import android.provider.MediaStore; -import android.text.TextUtils; -import android.util.Log; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -public final class WebActivityMapper { - private static final String LOGTAG = "Gecko"; - - private static final Map<String, WebActivityMapping> activityMap = new HashMap<String, WebActivityMapping>(); - static { - activityMap.put("dial", new DialMapping()); - activityMap.put("open", new OpenMapping()); - activityMap.put("pick", new PickMapping()); - activityMap.put("send", new SendMapping()); - activityMap.put("view", new ViewMapping()); - activityMap.put("record", new RecordMapping()); - }; - - private static abstract class WebActivityMapping { - protected JSONObject mData; - - public void setData(JSONObject data) { - mData = data; - } - - // Cannot return null - public abstract String getAction(); - - public String getMime() throws JSONException { - return null; - } - - public String getUri() throws JSONException { - return null; - } - - public void putExtras(Intent intent) throws JSONException {} - } - - /** - * Provides useful defaults for mime type and uri. - */ - private static abstract class BaseMapping extends WebActivityMapping { - /** - * If 'type' is present in data object, uses the value as the MIME type. - */ - @Override - public String getMime() throws JSONException { - return mData.optString("type", null); - } - - /** - * If 'uri' or 'url' is present in data object, uses the respective value as the Uri. - */ - @Override - public String getUri() throws JSONException { - // Will return uri or url if present. - String uri = mData.optString("uri", null); - return uri != null ? uri : mData.optString("url", null); - } - } - - public static Intent getIntentForWebActivity(JSONObject message) throws JSONException { - final String name = message.getString("name").toLowerCase(); - final JSONObject data = message.getJSONObject("data"); - - Log.w(LOGTAG, "Activity is: " + name); - final WebActivityMapping mapping = activityMap.get(name); - if (mapping == null) { - Log.w(LOGTAG, "No mapping found!"); - return null; - } - - mapping.setData(data); - - final Intent intent = new Intent(mapping.getAction()); - - final String mime = mapping.getMime(); - if (!TextUtils.isEmpty(mime)) { - intent.setType(mime); - } - - final String uri = mapping.getUri(); - if (!TextUtils.isEmpty(uri)) { - intent.setData(Uri.parse(uri)); - } - - mapping.putExtras(intent); - - return intent; - } - - private static class DialMapping extends WebActivityMapping { - @Override - public String getAction() { - return Intent.ACTION_DIAL; - } - - @Override - public String getUri() throws JSONException { - return "tel:" + mData.getString("number"); - } - } - - private static class OpenMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_VIEW; - } - } - - private static class PickMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_GET_CONTENT; - } - - @Override - public String getMime() throws JSONException { - // bug 1007112 - pick action needs a mimetype to work - String mime = mData.optString("type", null); - return !TextUtils.isEmpty(mime) ? mime : "*/*"; - } - } - - private static class SendMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_SEND; - } - - @Override - public void putExtras(Intent intent) throws JSONException { - optPutExtra("text", Intent.EXTRA_TEXT, intent); - optPutExtra("html_text", Intent.EXTRA_HTML_TEXT, intent); - optPutExtra("stream", Intent.EXTRA_STREAM, intent); - } - - private void optPutExtra(String key, String extraName, Intent intent) { - final String extraValue = mData.optString(key); - if (!TextUtils.isEmpty(extraValue)) { - intent.putExtra(extraName, extraValue); - } - } - } - - private static class ViewMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_VIEW; - } - - @Override - public String getMime() { - // MozActivity adds a type 'url' here, we don't want to set the MIME to 'url'. - String type = mData.optString("type", null); - if ("url".equals(type) || "uri".equals(type)) { - return null; - } else { - return type; - } - } - } - - private static class RecordMapping extends WebActivityMapping { - @Override - public String getAction() { - String type = mData.optString("type", null); - if ("photos".equals(type)) { - return "android.media.action.IMAGE_CAPTURE"; - } else if ("videos".equals(type)) { - return "android.media.action.VIDEO_CAPTURE"; - } - return null; - } - - // Add an extra to specify where to save the picture/video. - @Override - public void putExtras(Intent intent) { - final String action = getAction(); - - final String dirType = action == "android.media.action.IMAGE_CAPTURE" - ? Environment.DIRECTORY_PICTURES - : Environment.DIRECTORY_MOVIES; - - final String ext = action == "android.media.action.IMAGE_CAPTURE" - ? ".jpg" - : ".mp4"; - - File destDir = Environment.getExternalStoragePublicDirectory(dirType); - - try { - File dest = File.createTempFile( - "capture", /* prefix */ - ext, /* suffix */ - destDir /* directory */ - ); - intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(dest)); - } catch (Exception e) { - Log.w(LOGTAG, "Failed to add extra for " + action + " : " + e); - } - } - } -}
--- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -109,27 +109,25 @@ @BINPATH@/components/content_geckomediaplugins.xpt @BINPATH@/components/content_html.xpt @BINPATH@/components/content_webrtc.xpt @BINPATH@/components/content_xslt.xpt @BINPATH@/components/cookie.xpt @BINPATH@/components/directory.xpt @BINPATH@/components/docshell.xpt @BINPATH@/components/dom.xpt -@BINPATH@/components/dom_activities.xpt @BINPATH@/components/dom_apps.xpt @BINPATH@/components/dom_newapps.xpt @BINPATH@/components/dom_base.xpt @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_core.xpt @BINPATH@/components/dom_css.xpt @BINPATH@/components/dom_events.xpt @BINPATH@/components/dom_geolocation.xpt @BINPATH@/components/dom_media.xpt -@BINPATH@/components/dom_messages.xpt @BINPATH@/components/dom_network.xpt @BINPATH@/components/dom_notification.xpt @BINPATH@/components/dom_html.xpt @BINPATH@/components/dom_offline.xpt @BINPATH@/components/dom_json.xpt @BINPATH@/components/dom_power.xpt #ifdef MOZ_ANDROID_GCM @BINPATH@/components/dom_push.xpt @@ -367,23 +365,16 @@ @BINPATH@/components/XULStore.manifest @BINPATH@/components/Webapps.js @BINPATH@/components/Webapps.manifest @BINPATH@/components/AppsService.js @BINPATH@/components/AppsService.manifest @BINPATH@/components/htmlMenuBuilder.js @BINPATH@/components/htmlMenuBuilder.manifest -@BINPATH@/components/Activities.manifest -@BINPATH@/components/AndroidActivitiesGlue.js -@BINPATH@/components/ActivityProxy.js -@BINPATH@/components/ActivityRequestHandler.js -@BINPATH@/components/ActivityWrapper.js -@BINPATH@/components/ActivityMessageConfigurator.js - @BINPATH@/components/SystemMessageInternal.js @BINPATH@/components/SystemMessageManager.js @BINPATH@/components/SystemMessageCache.js @BINPATH@/components/SystemMessageManager.manifest @BINPATH@/components/InstallPackagedWebapp.manifest @BINPATH@/components/InstallPackagedWebapp.js @@ -426,24 +417,22 @@ @BINPATH@/components/PACGenerator.manifest @BINPATH@/components/TVSimulatorService.js @BINPATH@/components/TVSimulatorService.manifest ; Modules @BINPATH@/modules/* -#ifdef MOZ_SAFE_BROWSING ; Safe Browsing @BINPATH@/components/nsURLClassifier.manifest @BINPATH@/components/nsUrlClassifierHashCompleter.js @BINPATH@/components/nsUrlClassifierListManager.js @BINPATH@/components/nsUrlClassifierLib.js @BINPATH@/components/url-classifier.xpt -#endif ; Private Browsing @BINPATH@/components/privatebrowsing.xpt @BINPATH@/components/PrivateBrowsing.manifest @BINPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js ; Security Reports @BINPATH@/components/SecurityReporter.manifest
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java @@ -1,21 +1,24 @@ /* 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/. */ package org.mozilla.gecko.background.fxa; import org.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse; +import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse; import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; -import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse; import org.mozilla.gecko.background.fxa.FxAccountClient20.TwoKeys; import org.mozilla.gecko.fxa.FxAccountDevice; import org.mozilla.gecko.sync.ExtendedJSONObject; +import java.util.List; + public interface FxAccountClient { public void accountStatus(String uid, RequestDelegate<AccountStatusResponse> requestDelegate); public void recoveryEmailStatus(byte[] sessionToken, RequestDelegate<RecoveryEmailStatusResponse> requestDelegate); public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate); public void sign(byte[] sessionToken, ExtendedJSONObject publicKey, long certificateDurationInMilliseconds, RequestDelegate<String> requestDelegate); public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate<FxAccountDevice> requestDelegate); public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate); + public void notifyDevices(byte[] sessionToken, List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> requestDelegate); }
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java @@ -1,14 +1,16 @@ /* 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/. */ package org.mozilla.gecko.background.fxa; +import android.support.annotation.NonNull; + import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientMalformedResponseException; import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.Locales; import org.mozilla.gecko.fxa.FxAccountDevice; @@ -28,16 +30,17 @@ import java.io.UnsupportedEncodingExcept import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Executor; import javax.crypto.Mac; import ch.boye.httpclientandroidlib.HttpEntity; @@ -827,17 +830,16 @@ public class FxAccountClient20 implement try { HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey); } catch (Exception e) { invokeHandleError(delegate, e); return; } final BaseResource resource; - final ExtendedJSONObject body; try { resource = getBaseResource("account/devices"); } catch (URISyntaxException | UnsupportedEncodingException e) { invokeHandleError(delegate, e); return; } resource.delegate = new ResourceDelegate<FxAccountDevice[]>(resource, delegate, ResponseType.JSON_ARRAY, tokenId, reqHMACKey) { @@ -853,9 +855,60 @@ public class FxAccountClient20 implement } catch (Exception e) { delegate.handleError(e); } } }; resource.get(); } + + @Override + public void notifyDevices(@NonNull byte[] sessionToken, @NonNull List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> delegate) { + final byte[] tokenId = new byte[32]; + final byte[] reqHMACKey = new byte[32]; + final byte[] requestKey = new byte[32]; + try { + HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey); + } catch (Exception e) { + invokeHandleError(delegate, e); + return; + } + + final BaseResource resource; + final ExtendedJSONObject body = createNotifyDevicesBody(deviceIds, payload, TTL); + try { + resource = getBaseResource("account/devices/notify"); + } catch (URISyntaxException | UnsupportedEncodingException e) { + invokeHandleError(delegate, e); + return; + } + + resource.delegate = new ResourceDelegate<ExtendedJSONObject>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { + @Override + public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { + try { + delegate.handleSuccess(body); + } catch (Exception e) { + delegate.handleError(e); + } + } + }; + + post(resource, body); + } + + @NonNull + @SuppressWarnings("unchecked") + private ExtendedJSONObject createNotifyDevicesBody(@NonNull List<String> deviceIds, ExtendedJSONObject payload, Long TTL) { + final ExtendedJSONObject body = new ExtendedJSONObject(); + final JSONArray to = new JSONArray(); + to.addAll(deviceIds); + body.put("to", to); + if (payload != null) { + body.put("payload", payload); + } + if (TTL != null) { + body.put("TTL", TTL); + } + return body; + } }
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java @@ -14,19 +14,18 @@ import android.util.Log; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountClient; import org.mozilla.gecko.background.fxa.FxAccountClient20; import org.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse; import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; import org.mozilla.gecko.background.fxa.FxAccountRemoteError; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; +import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount.InvalidFxAState; import org.mozilla.gecko.fxa.login.State; -import org.mozilla.gecko.fxa.login.State.StateLabel; -import org.mozilla.gecko.fxa.login.TokensAndKeysState; import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate; import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -107,17 +106,21 @@ public class FxAccountDeviceRegistrator } private static void doFxaRegistration(final Context context, final Bundle subscription, final boolean allowRecursion) throws InvalidFxAState { String pushCallback = subscription.getString("pushCallback"); String pushPublicKey = subscription.getString("pushPublicKey"); String pushAuthKey = subscription.getString("pushAuthKey"); final AndroidFxAccount fxAccount = AndroidFxAccount.fromContext(context); - final byte[] sessionToken = getSessionToken(fxAccount); + if (fxAccount == null) { + Log.e(LOG_TAG, "AndroidFxAccount is null"); + return; + } + final byte[] sessionToken = fxAccount.getSessionToken(); final FxAccountDevice device; String deviceId = fxAccount.getDeviceId(); String clientName = getClientName(fxAccount, context); if (TextUtils.isEmpty(deviceId)) { Log.i(LOG_TAG, "Attempting registration for a new device"); device = FxAccountDevice.forRegister(clientName, "mobile", pushCallback, pushPublicKey, pushAuthKey); } else { Log.i(LOG_TAG, "Attempting registration for an existing device"); @@ -175,27 +178,16 @@ public class FxAccountDeviceRegistrator new SharedPreferencesClientsDataDelegate(fxAccount.getSyncPrefs(), context); return clientsDataDelegate.getClientName(); } catch (UnsupportedEncodingException | GeneralSecurityException e) { Log.e(LOG_TAG, "Unable to get client name.", e); return null; } } - @Nullable - private static byte[] getSessionToken(final AndroidFxAccount fxAccount) throws InvalidFxAState { - State state = fxAccount.getState(); - StateLabel stateLabel = state.getStateLabel(); - if (stateLabel == StateLabel.Cohabiting || stateLabel == StateLabel.Married) { - TokensAndKeysState tokensAndKeysState = (TokensAndKeysState) state; - return tokensAndKeysState.getSessionToken(); - } - throw new InvalidFxAState("Cannot get sessionToken: not in a TokensAndKeysState state"); - } - private static void handleTokenError(final FxAccountClientRemoteException error, final FxAccountClient fxAccountClient, final AndroidFxAccount fxAccount) { Log.i(LOG_TAG, "Recovering from invalid token error: ", error); logErrorAndResetDeviceRegistrationVersion(error, fxAccount); fxAccountClient.accountStatus(fxAccount.getState().uid, new RequestDelegate<AccountStatusResponse>() { @Override @@ -282,17 +274,9 @@ public class FxAccountDeviceRegistrator // We have no choice but to use reflection here, sorry :( Class<?> eventDispatcher = Class.forName("org.mozilla.gecko.EventDispatcher"); Method getInstance = eventDispatcher.getMethod("getInstance"); Object instance = getInstance.invoke(null); Method registerBackgroundThreadListener = eventDispatcher.getMethod("registerBackgroundThreadListener", BundleEventListener.class, String[].class); registerBackgroundThreadListener.invoke(instance, this, new String[] { "FxAccountsPush:Subscribe:Response" }); } - - public static class InvalidFxAState extends Exception { - private static final long serialVersionUID = -8537626959811195978L; - - public InvalidFxAState(String message) { - super(message); - } - } }
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountPushHandler.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountPushHandler.java @@ -2,24 +2,28 @@ package org.mozilla.gecko.fxa; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; public class FxAccountPushHandler { private static final String LOG_TAG = "FxAccountPush"; private static final String COMMAND_DEVICE_DISCONNECTED = "fxaccounts:device_disconnected"; + private static final String COMMAND_COLLECTION_CHANGED = "sync:collection_changed"; + + private static final String CLIENTS_COLLECTION = "clients"; // Forbid instantiation private FxAccountPushHandler() {} public static void handleFxAPushMessage(Context context, Bundle bundle) { Log.i(LOG_TAG, "Handling FxA Push Message"); String rawMessage = bundle.getString("message"); JSONObject message = null; @@ -40,25 +44,45 @@ public class FxAccountPushHandler { } try { String command = message.getString("command"); JSONObject data = message.getJSONObject("data"); switch (command) { case COMMAND_DEVICE_DISCONNECTED: handleDeviceDisconnection(context, data); break; + case COMMAND_COLLECTION_CHANGED: + handleCollectionChanged(context, data); + break; default: Log.d(LOG_TAG, "No handler defined for FxA Push command " + command); break; } } catch (JSONException e) { Log.e(LOG_TAG, "Error while handling FxA push notification", e); } } + private static void handleCollectionChanged(Context context, JSONObject data) throws JSONException { + JSONArray collections = data.getJSONArray("collections"); + int len = collections.length(); + for (int i = 0; i < len; i++) { + if (collections.getString(i).equals(CLIENTS_COLLECTION)) { + final Account account = FirefoxAccounts.getFirefoxAccount(context); + if (account == null) { + Log.e(LOG_TAG, "The account does not exist anymore"); + return; + } + final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); + fxAccount.requestImmediateSync(new String[] { CLIENTS_COLLECTION }, null); + return; + } + } + } + private static void handleDeviceDisconnection(Context context, JSONObject data) throws JSONException { final Account account = FirefoxAccounts.getFirefoxAccount(context); if (account == null) { Log.e(LOG_TAG, "The account does not exist anymore"); return; } final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); if (!fxAccount.getDeviceId().equals(data.getString("id"))) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java @@ -25,16 +25,17 @@ import org.mozilla.gecko.background.comm import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountUtils; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.fxa.login.State; import org.mozilla.gecko.fxa.login.State.StateLabel; import org.mozilla.gecko.fxa.login.StateFactory; +import org.mozilla.gecko.fxa.login.TokensAndKeysState; import org.mozilla.gecko.fxa.sync.FxAccountProfileService; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.setup.Constants; import org.mozilla.gecko.util.ThreadUtils; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; @@ -602,16 +603,34 @@ public class AndroidFxAccount { StateLabel stateLabel = StateLabel.valueOf(stateLabelString); Logger.debug(LOG_TAG, "Account is in state " + stateLabel); return StateFactory.fromJSONObject(stateLabel, new ExtendedJSONObject(stateString)); } catch (Exception e) { throw new IllegalStateException("could not get state", e); } } + public byte[] getSessionToken() throws InvalidFxAState { + State state = getState(); + StateLabel stateLabel = state.getStateLabel(); + if (stateLabel == StateLabel.Cohabiting || stateLabel == StateLabel.Married) { + TokensAndKeysState tokensAndKeysState = (TokensAndKeysState) state; + return tokensAndKeysState.getSessionToken(); + } + throw new InvalidFxAState("Cannot get sessionToken: not in a TokensAndKeysState state"); + } + + public static class InvalidFxAState extends Exception { + private static final long serialVersionUID = -8537626959811195978L; + + public InvalidFxAState(String message) { + super(message); + } + } + /** * <b>For debugging only!</b> */ public void dump() { if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) { return; } ExtendedJSONObject o = toJSONObject();
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java @@ -1,29 +1,37 @@ /* 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/. */ package org.mozilla.gecko.sync.stage; import android.accounts.Account; import android.content.Context; +import android.support.annotation.NonNull; import android.text.TextUtils; +import android.util.Log; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.background.common.log.Logger; +import org.mozilla.gecko.background.fxa.FxAccountClient; +import org.mozilla.gecko.background.fxa.FxAccountClient20; +import org.mozilla.gecko.background.fxa.FxAccountClientException; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; import org.mozilla.gecko.sync.CommandProcessor; import org.mozilla.gecko.sync.CommandProcessor.Command; import org.mozilla.gecko.sync.CryptoRecord; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.HTTPFailureException; import org.mozilla.gecko.sync.NoCollectionKeysSetException; @@ -49,28 +57,29 @@ import ch.boye.httpclientandroidlib.Http public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { private static final String LOG_TAG = "SyncClientsEngineStage"; public static final String COLLECTION_NAME = "clients"; public static final String STAGE_NAME = COLLECTION_NAME; public static final int CLIENTS_TTL_REFRESH = 604800000; // 7 days in milliseconds. public static final int MAX_UPLOAD_FAILURE_COUNT = 5; + public static final long NOTIFY_TAB_SENT_TTL_SECS = TimeUnit.SECONDS.convert(1L, TimeUnit.HOURS); // 1 hour protected final ClientRecordFactory factory = new ClientRecordFactory(); protected ClientUploadDelegate clientUploadDelegate; protected ClientDownloadDelegate clientDownloadDelegate; // Be sure to use this safely via getClientsDatabaseAccessor/closeDataAccessor. protected ClientsDatabaseAccessor db; protected volatile boolean shouldWipe; protected volatile boolean shouldUploadLocalRecord; // Set if, e.g., we received commands or need to refresh our version. protected final AtomicInteger uploadAttemptsCount = new AtomicInteger(); - protected final List<ClientRecord> toUpload = new ArrayList<ClientRecord>(); + protected final List<ClientRecord> modifiedClientsToUpload = new ArrayList<ClientRecord>(); protected int getClientsCount() { return getClientsDatabaseAccessor().clientsCount(); } protected synchronized ClientsDatabaseAccessor getClientsDatabaseAccessor() { if (db == null) { db = new ClientsDatabaseAccessor(session.getContext()); @@ -146,23 +155,90 @@ public class SyncClientsEngineStage exte Logger.debug(LOG_TAG, "Server response asserts " + response.weaveRecords() + " records."); // TODO: persist the response timestamp to know whether to download next time (Bug 726055). clientUploadDelegate = new ClientUploadDelegate(); clientsDelegate.setClientsCount(clientsCount); // If we upload remote records, checkAndUpload() will be called upon // upload success in the delegate. Otherwise call checkAndUpload() now. - if (toUpload.size() > 0) { + if (modifiedClientsToUpload.size() > 0) { + // modifiedClientsToUpload is cleared in uploadRemoteRecords, save what we need here + final List<String> devicesToNotify = new ArrayList<>(); + for (ClientRecord record : modifiedClientsToUpload) { + if (!TextUtils.isEmpty(record.fxaDeviceId)) { + devicesToNotify.add(record.fxaDeviceId); + } + } + + // This method is synchronous, there's no risk of notifying the clients + // before we actually uploaded the records uploadRemoteRecords(); + + // Notify the clients who got their record written + notifyClients(devicesToNotify); + return; } checkAndUpload(); } + private void notifyClients(final List<String> devicesToNotify) { + final ExecutorService executor = Executors.newSingleThreadExecutor(); + final Context context = session.getContext(); + final Account account = FirefoxAccounts.getFirefoxAccount(context); + if (account == null) { + Log.e(LOG_TAG, "Can't notify other clients: no account"); + return; + } + final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); + final ExtendedJSONObject payload = createNotifyDevicesPayload(); + + final byte[] sessionToken; + try { + sessionToken = fxAccount.getSessionToken(); + } catch (AndroidFxAccount.InvalidFxAState invalidFxAState) { + Log.e(LOG_TAG, "Could not get session token", invalidFxAState); + return; + } + + // API doc : https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#post-v1accountdevicesnotify + final FxAccountClient fxAccountClient = new FxAccountClient20(fxAccount.getAccountServerURI(), executor); + fxAccountClient.notifyDevices(sessionToken, devicesToNotify, payload, NOTIFY_TAB_SENT_TTL_SECS, new FxAccountClient20.RequestDelegate<ExtendedJSONObject>() { + @Override + public void handleError(Exception e) { + Log.e(LOG_TAG, "Error while notifying devices", e); + } + + @Override + public void handleFailure(FxAccountClientException.FxAccountClientRemoteException e) { + Log.e(LOG_TAG, "Error while notifying devices", e); + } + + @Override + public void handleSuccess(ExtendedJSONObject result) { + Log.i(LOG_TAG, devicesToNotify.size() + " devices notified"); + } + }); + } + + @NonNull + @SuppressWarnings("unchecked") + private ExtendedJSONObject createNotifyDevicesPayload() { + final ExtendedJSONObject payload = new ExtendedJSONObject(); + payload.put("version", 1); + payload.put("command", "sync:collection_changed"); + final ExtendedJSONObject data = new ExtendedJSONObject(); + final JSONArray collections = new JSONArray(); + collections.add("clients"); + data.put("collections", collections); + payload.put("data", data); + return payload; + } + @Override public void handleRequestFailure(SyncStorageResponse response) { BaseResource.consumeEntity(response); // We don't need the response at all, and any exception handling shouldn't need the response body. localAccountGUIDDownloaded = false; try { Logger.info(LOG_TAG, "Client upload failed. Aborting sync."); session.abort(new HTTPFailureException(response), "Client download failed."); @@ -285,17 +361,17 @@ public class SyncClientsEngineStage exte // If upload failed because of `ifUnmodifiedSince` then there are new // commands uploaded to our record. We must download and process them first. if (!shouldUploadLocalRecord || statusCode == HttpStatus.SC_PRECONDITION_FAILED || uploadAttemptsCount.incrementAndGet() > MAX_UPLOAD_FAILURE_COUNT) { Logger.debug(LOG_TAG, "Client upload failed. Aborting sync."); if (!currentlyUploadingLocalRecord) { - toUpload.clear(); // These will be redownloaded. + modifiedClientsToUpload.clear(); // These will be redownloaded. } BaseResource.consumeEntity(response); // The exception thrown should need the response body. session.abort(new HTTPFailureException(response), "Client upload failed."); return; } Logger.trace(LOG_TAG, "Retrying upload…"); // Preconditions: // shouldUploadLocalRecord == true && @@ -469,41 +545,41 @@ public class SyncClientsEngineStage exte for (Command command : commands) { JSONObject jsonCommand = command.asJSONObject(); if (record.commands == null) { record.commands = new JSONArray(); } record.commands.add(jsonCommand); } - toUpload.add(record); + modifiedClientsToUpload.add(record); } @SuppressWarnings("unchecked") protected void uploadRemoteRecords() { - Logger.trace(LOG_TAG, "In uploadRemoteRecords. Uploading " + toUpload.size() + " records" ); + Logger.trace(LOG_TAG, "In uploadRemoteRecords. Uploading " + modifiedClientsToUpload.size() + " records" ); - for (ClientRecord r : toUpload) { + for (ClientRecord r : modifiedClientsToUpload) { Logger.trace(LOG_TAG, ">> Uploading record " + r.guid + ": " + r.name); } - if (toUpload.size() == 1) { - ClientRecord record = toUpload.get(0); + if (modifiedClientsToUpload.size() == 1) { + ClientRecord record = modifiedClientsToUpload.get(0); Logger.debug(LOG_TAG, "Only 1 remote record to upload."); Logger.debug(LOG_TAG, "Record last modified: " + record.lastModified); CryptoRecord cryptoRecord = encryptClientRecord(record); if (cryptoRecord != null) { clientUploadDelegate.setUploadDetails(false); this.uploadClientRecord(cryptoRecord); } return; } JSONArray cryptoRecords = new JSONArray(); - for (ClientRecord record : toUpload) { + for (ClientRecord record : modifiedClientsToUpload) { Logger.trace(LOG_TAG, "Record " + record.guid + " is being uploaded" ); CryptoRecord cryptoRecord = encryptClientRecord(record); cryptoRecords.add(cryptoRecord.toJSONObject()); } Logger.debug(LOG_TAG, "Uploading records: " + cryptoRecords.size()); clientUploadDelegate.setUploadDetails(false); this.uploadClientRecords(cryptoRecords); @@ -542,17 +618,17 @@ public class SyncClientsEngineStage exte session.abort(e, encryptionFailure); } return null; } public void clearRecordsToUpload() { try { getClientsDatabaseAccessor().wipeCommandsTable(); - toUpload.clear(); + modifiedClientsToUpload.clear(); } finally { closeDataAccessor(); } } protected void downloadClientRecords() { shouldWipe = true; clientDownloadDelegate = makeClientDownloadDelegate();
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java @@ -760,36 +760,36 @@ public class TestClientsEngineStage exte public void testAddCommandsToUnversionedClient() throws NullCursorException { db = new TestAddCommandsMockClientsDatabaseAccessor(); final ClientRecord remoteRecord = new ClientRecord(); remoteRecord.version = null; final String expectedGUID = remoteRecord.guid; this.addCommands(remoteRecord); - assertEquals(1, toUpload.size()); + assertEquals(1, modifiedClientsToUpload.size()); - final ClientRecord recordToUpload = toUpload.get(0); + final ClientRecord recordToUpload = modifiedClientsToUpload.get(0); assertEquals(4, recordToUpload.commands.size()); assertEquals(expectedGUID, recordToUpload.guid); assertEquals(null, recordToUpload.version); } @Test public void testAddCommandsToVersionedClient() throws NullCursorException { db = new TestAddCommandsMockClientsDatabaseAccessor(); final ClientRecord remoteRecord = new ClientRecord(); remoteRecord.version = "12a1"; final String expectedGUID = remoteRecord.guid; this.addCommands(remoteRecord); - assertEquals(1, toUpload.size()); + assertEquals(1, modifiedClientsToUpload.size()); - final ClientRecord recordToUpload = toUpload.get(0); + final ClientRecord recordToUpload = modifiedClientsToUpload.get(0); assertEquals(4, recordToUpload.commands.size()); assertEquals(expectedGUID, recordToUpload.guid); assertEquals("12a1", recordToUpload.version); } @Test public void testLastModifiedTimestamp() throws NullCursorException { // If we uploaded a record a moment ago, we shouldn't upload another.
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java @@ -1,16 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.gecko.fxa.login; import android.text.TextUtils; import org.mozilla.gecko.background.fxa.FxAccountClient; +import org.mozilla.gecko.background.fxa.FxAccountClient20; import org.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse; import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse; import org.mozilla.gecko.background.fxa.FxAccountClient20.TwoKeys; import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse; import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; import org.mozilla.gecko.background.fxa.FxAccountRemoteError; import org.mozilla.gecko.background.fxa.FxAccountUtils; @@ -18,16 +19,17 @@ import org.mozilla.gecko.fxa.FxAccountDe import org.mozilla.gecko.browserid.MockMyIDTokenFactory; import org.mozilla.gecko.browserid.RSACryptoImplementation; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.Utils; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import ch.boye.httpclientandroidlib.HttpStatus; import ch.boye.httpclientandroidlib.ProtocolVersion; import ch.boye.httpclientandroidlib.entity.StringEntity; import ch.boye.httpclientandroidlib.message.BasicHttpResponse; @@ -211,9 +213,14 @@ public class MockFxAccountClient impleme if (!user.verified) { handleFailure(requestDelegate, HttpStatus.SC_BAD_REQUEST, FxAccountRemoteError.ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT, "user is unverified"); return; } Collection<FxAccountDevice> devices = user.devices.values(); FxAccountDevice[] devicesArray = devices.toArray(new FxAccountDevice[devices.size()]); requestDelegate.handleSuccess(devicesArray); } + + @Override + public void notifyDevices(byte[] sessionToken, List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> requestDelegate) { + requestDelegate.handleSuccess(new ExtendedJSONObject()); + } }
--- a/old-configure.in +++ b/old-configure.in @@ -2334,17 +2334,16 @@ VPX_USE_YASM= VPX_ASFLAGS= VPX_AS_CONVERSION= VPX_X86_ASM= VPX_ARM_ASM= LIBJPEG_TURBO_AS= LIBJPEG_TURBO_ASFLAGS= MOZ_PREF_EXTENSIONS=1 MOZ_REFLOW_PERF= -MOZ_SAFE_BROWSING= MOZ_SPELLCHECK=1 MOZ_TOOLKIT_SEARCH=1 MOZ_UI_LOCALE=en-US MOZ_UNIVERSALCHARDET=1 MOZ_URL_CLASSIFIER= MOZ_XUL=1 MOZ_ZIPWRITER=1 MOZ_NO_SMART_CARDS= @@ -2490,17 +2489,17 @@ AC_SUBST(MOZ_B2G_VERSION) dnl ======================================================== dnl Ensure Android SDK and build-tools versions depending on dnl mobile target. dnl ======================================================== if test -z "$gonkdir" ; then case "$MOZ_BUILD_APP" in mobile/android) - MOZ_ANDROID_SDK(23, 23.0.3) + MOZ_ANDROID_SDK(23, "23.0.3 23.0.1") ;; esac fi dnl ======================================================== dnl = dnl = Toolkit Options dnl = @@ -4300,34 +4299,18 @@ then fi if test -n "$MOZ_SYSTEM_SQLITE"; then AC_DEFINE(MOZ_SYSTEM_SQLITE) fi AC_SUBST(MOZ_SYSTEM_SQLITE) dnl ======================================================== -dnl = Enable safe browsing (anti-phishing) -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(safe-browsing, -[ --enable-safe-browsing Enable safe browsing (anti-phishing) implementation], - MOZ_SAFE_BROWSING=1, - MOZ_SAFE_BROWSING= ) -if test -n "$MOZ_SAFE_BROWSING"; then - AC_DEFINE(MOZ_SAFE_BROWSING) -fi -AC_SUBST(MOZ_SAFE_BROWSING) - -dnl ======================================================== dnl = Enable url-classifier dnl ======================================================== -dnl Implicitly enabled by default if building with safe-browsing -if test -n "$MOZ_SAFE_BROWSING"; then - MOZ_URL_CLASSIFIER=1 -fi MOZ_ARG_ENABLE_BOOL(url-classifier, [ --enable-url-classifier Enable url classifier module], MOZ_URL_CLASSIFIER=1, MOZ_URL_CLASSIFIER= ) if test -n "$MOZ_URL_CLASSIFIER"; then AC_DEFINE(MOZ_URL_CLASSIFIER) fi AC_SUBST(MOZ_URL_CLASSIFIER)
--- a/python/mozboot/mozboot/bootstrap.py +++ b/python/mozboot/mozboot/bootstrap.py @@ -412,17 +412,17 @@ def current_firefox_checkout(check_outpu path = os.getcwd() while path: hg_dir = os.path.join(path, '.hg') git_dir = os.path.join(path, '.git') if hg and os.path.exists(hg_dir): # Verify the hg repo is a Firefox repo by looking at rev 0. try: - node = check_output([hg, 'log', '-r', '0', '-T', '{node}'], cwd=path) + node = check_output([hg, 'log', '-r', '0', '--template', '{node}'], cwd=path) if node in HG_ROOT_REVISIONS: return 'hg' # Else the root revision is different. There could be nested # repos. So keep traversing the parents. except subprocess.CalledProcessError: pass # TODO check git remotes or `git rev-parse -q --verify $sha1^{commit}`
--- a/python/mozboot/mozboot/centosfedora.py +++ b/python/mozboot/mozboot/centosfedora.py @@ -68,16 +68,17 @@ class CentOSFedoraBootstrapper(BaseBoots 'python2-devel', ] self.browser_packages += [ 'gcc-c++', ] self.mobile_android_packages += [ + 'java-1.8.0-openjdk-devel', 'ncurses-devel.i686', 'libstdc++.i686', 'zlib-devel.i686', ] def install_system_packages(self): self.dnf_groupinstall(*self.group_packages) self.dnf_install(*self.packages) @@ -125,16 +126,25 @@ class CentOSFedoraBootstrapper(BaseBoots self.sdk_url = 'https://dl.google.com/android/android-sdk_r24.0.1-linux.tgz' self.ndk_url = android.android_ndk_url('linux') android.ensure_android_sdk_and_ndk(path=mozbuild_path, sdk_path=self.sdk_path, sdk_url=self.sdk_url, ndk_path=self.ndk_path, ndk_url=self.ndk_url, artifact_mode=artifact_mode) + # Most recent version of build-tools appears to be 23.0.1 on Fedora + packages = [p for p in android.ANDROID_PACKAGES if not p.startswith('build-tools')] + packages.append('build-tools-23.0.1') + + # 3. We expect the |android| tool to be at + # ~/.mozbuild/android-sdk-linux/tools/android. + android_tool = os.path.join(self.sdk_path, 'tools', 'android') + android.ensure_android_packages(android_tool=android_tool, packages=packages) + def suggest_mobile_android_mozconfig(self, artifact_mode=False): import android android.suggest_mozconfig(sdk_path=self.sdk_path, ndk_path=self.ndk_path, artifact_mode=artifact_mode) def suggest_mobile_android_artifact_mode_mozconfig(self): self.suggest_mobile_android_mozconfig(artifact_mode=True)
--- a/python/mozlint/mozlint/vcs.py +++ b/python/mozlint/mozlint/vcs.py @@ -44,17 +44,17 @@ class VCSFiles(object): return self.vcs == 'git' def _run(self, cmd): files = subprocess.check_output(cmd).split() return [os.path.join(self.root, f) for f in files] def by_rev(self, rev): if self.is_hg: - return self._run(['hg', 'log', '-T', '{files % "\\n{file}"}', '-r', rev]) + return self._run(['hg', 'log', '--template', '{files % "\\n{file}"}', '-r', rev]) elif self.is_git: return self._run(['git', 'diff', '--name-only', rev]) return [] def by_workdir(self): if self.is_hg: return self._run(['hg', 'status', '-amn']) elif self.is_git:
--- a/testing/firefox-ui/tests/puppeteer/test_appinfo.py +++ b/testing/firefox-ui/tests/puppeteer/test_appinfo.py @@ -11,17 +11,18 @@ class TestAppInfo(FirefoxTestCase): def test_valid_properties(self): binary = self.marionette.bin version_info = mozversion.get_version(binary=binary) self.assertEqual(self.appinfo.ID, version_info['application_id']) self.assertEqual(self.appinfo.name, version_info['application_name']) self.assertEqual(self.appinfo.vendor, version_info['application_vendor']) self.assertEqual(self.appinfo.version, version_info['application_version']) - self.assertEqual(self.appinfo.platformBuildID, version_info['platform_buildid']) + # Bug 1298328 - Platform buildid mismatch due to incremental builds + # self.assertEqual(self.appinfo.platformBuildID, version_info['platform_buildid']) self.assertEqual(self.appinfo.platformVersion, version_info['platform_version']) self.assertIsNotNone(self.appinfo.locale) self.assertIsNotNone(self.appinfo.user_agent) self.assertIsNotNone(self.appinfo.XPCOMABI) def test_invalid_properties(self): with self.assertRaises(AttributeError): self.appinfo.unknown
--- a/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js +++ b/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js @@ -78,16 +78,33 @@ ExtensionTestUtils.loadExtension = funct } else { messageQueue.add([msg, ...args]); checkMessages(); } }, }; + // Mimic serialization of functions as done in `Extension.generateXPI` and + // `Extension.generateZipFile` because functions are dropped when `ext` object + // is sent to the main process via the message manager. + ext = Object.assign({}, ext); + if (ext.files) { + ext.files = Object.assign({}, ext.files); + for (let filename of Object.keys(ext.files)) { + let file = ext.files[filename]; + if (typeof file == "function") { + ext.files[filename] = `(${file})();` + } + } + } + if (typeof ext.background == "function") { + ext.background = `(${ext.background})();` + } + var extension = SpecialPowers.loadExtension(ext, handler); registerCleanup(() => { if (extension.state == "pending" || extension.state == "running") { SimpleTest.ok(false, "Extension left running at test shutdown") return extension.unload(); } else if (extension.state == "unloading") { SimpleTest.ok(false, "Extension not fully unloaded at test shutdown")
--- a/testing/web-platform/tests/web-animations/interfaces/AnimationEffectTiming/getComputedStyle.html +++ b/testing/web-platform/tests/web-animations/interfaces/AnimationEffectTiming/getComputedStyle.html @@ -102,22 +102,18 @@ test(function(t) { anim.currentTime = 1000; assert_equals(getComputedStyle(div).opacity, '0.9', 'set currentTime before endTime'); anim.currentTime = 5000; assert_equals(getComputedStyle(div).opacity, '0.5', 'set currentTime same as endTime'); - anim.currentTime = 9999; - assert_equals(getComputedStyle(div).opacity, '0.5', - 'set currentTime during duration'); - anim.currentTime = 10000; - assert_equals(getComputedStyle(div).opacity, '0.5', + assert_equals(getComputedStyle(div).opacity, '0', 'set currentTime after endTime'); }, 'change currentTime when fill forwards and endDelay is negative'); test(function(t) { var div = createDiv(t); var anim = div.animate({ opacity: [ 0, 1 ] }, { duration: 10000, direction: 'normal' });
--- a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/getComputedTiming.html +++ b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/getComputedTiming.html @@ -184,20 +184,20 @@ var gEndTimeTests = [ input: { duration: Infinity, iterations: 10, delay: -1000 }, expected: Infinity }, { desc: "an non-zero duration and negative delay", input: { duration: 1000, iterations: 2, delay: -1000 }, expected: 1000 }, { desc: "an non-zero duration and negative delay greater than active " + "duration", input: { duration: 1000, iterations: 2, delay: -3000 }, - expected: -1000 }, + expected: 0 }, { desc: "a zero duration and negative delay", input: { duration: 0, iterations: 2, delay: -1000 }, - expected: -1000 } + expected: 0 } ]; gEndTimeTests.forEach(function(stest) { test(function(t) { var effect = new KeyframeEffectReadOnly(target, { left: ["10px", "20px"] }, stest.input);
--- a/testing/web-platform/tests/web-animations/timing-model/animation-effects/active-time.html +++ b/testing/web-platform/tests/web-animations/timing-model/animation-effects/active-time.html @@ -95,17 +95,17 @@ test(function(t) { assert_times_equal(anim.effect.getComputedTiming().progress, 0.5); }, 'Active time in after phase with forwards fill and negative end delay' + ' is the active duration + end delay'); test(function(t) { var anim = createDiv(t).animate(null, { duration: 1000, iterations: 2.3, delay: 500, - endDelay: -3000, + endDelay: -2500, fill: 'forwards' }); anim.finish(); assert_equals(anim.effect.getComputedTiming().currentIteration, 0); assert_equals(anim.effect.getComputedTiming().progress, 0); }, 'Active time in after phase with forwards fill and negative end delay' + ' greater in magnitude than the active duration is zero'); test(function(t) {
--- a/testing/web-platform/tests/web-animations/timing-model/animation-effects/phases-and-states.html +++ b/testing/web-platform/tests/web-animations/timing-model/animation-effects/phases-and-states.html @@ -68,17 +68,17 @@ test(function(t) { assert_phase_at_time(animation, test.phase, test.currentTime); }); }, 'Phase calculation for an animation effect with a positive start delay'); test(function(t) { var animation = createDiv(t).animate(null, { duration: 1, delay: -1 }); [ { currentTime: -2, phase: 'before' }, - { currentTime: -1, phase: 'active' }, + { currentTime: -1, phase: 'before' }, { currentTime: 0, phase: 'after' } ] .forEach(function(test) { assert_phase_at_time(animation, test.phase, test.currentTime); }); }, 'Phase calculation for an animation effect with a negative start delay'); test(function(t) { var animation = createDiv(t).animate(null, { duration: 1, endDelay: 1 }); @@ -116,57 +116,61 @@ test(function(t) { }); }, 'Phase calculation for an animation effect with a negative end delay equal' + ' in magnitude to the active duration'); test(function(t) { var animation = createDiv(t).animate(null, { duration: 1, endDelay: -2 }); [ { currentTime: -2, phase: 'before' }, - { currentTime: -1, phase: 'after' } ] + { currentTime: -1, phase: 'before' }, + { currentTime: 0, phase: 'after' } ] .forEach(function(test) { assert_phase_at_time(animation, test.phase, test.currentTime); }); }, 'Phase calculation for an animation effect with a negative end delay' + ' greater in magnitude than the active duration'); test(function(t) { var animation = createDiv(t).animate(null, { duration: 2, delay: 1, endDelay: -1 }); - [ { currentTime: 0, phase: 'before' }, - { currentTime: 1, phase: 'active' }, + [ { currentTime: 0, phase: 'before' }, + { currentTime: 1, phase: 'active' }, { currentTime: 2, phase: 'after' } ] .forEach(function(test) { assert_phase_at_time(animation, test.phase, test.currentTime); }); }, 'Phase calculation for an animation effect with a positive start delay' + ' and a negative end delay lesser in magnitude than the active duration'); test(function(t) { var animation = createDiv(t).animate(null, { duration: 1, delay: -1, endDelay: -1 }); - [ { currentTime: -2, phase: 'before' }, - { currentTime: -1, phase: 'after' } ] + [ { currentTime: -2, phase: 'before' }, + { currentTime: -1, phase: 'before' }, + { currentTime: 0, phase: 'after' } ] .forEach(function(test) { assert_phase_at_time(animation, test.phase, test.currentTime); }); }, 'Phase calculation for an animation effect with a negative start delay' + ' and a negative end delay equal in magnitude to the active duration'); test(function(t) { var animation = createDiv(t).animate(null, { duration: 1, delay: -1, endDelay: -2 }); - [ { currentTime: -3, phase: 'before' }, - { currentTime: -2, phase: 'after' } ] + [ { currentTime: -3, phase: 'before' }, + { currentTime: -2, phase: 'before' }, + { currentTime: -1, phase: 'before' }, + { currentTime: 0, phase: 'after' } ] .forEach(function(test) { assert_phase_at_time(animation, test.phase, test.currentTime); }); }, 'Phase calculation for an animation effect with a negative start delay' + ' and a negative end delay equal greater in magnitude than the active' + ' duration'); test(function(t) {
--- a/toolkit/components/build/nsToolkitCompsModule.cpp +++ b/toolkit/components/build/nsToolkitCompsModule.cpp @@ -21,23 +21,21 @@ #include "nsDownloadManager.h" #include "DownloadPlatform.h" #include "nsDownloadProxy.h" #include "rdf.h" #include "nsTypeAheadFind.h" -#ifdef MOZ_URL_CLASSIFIER #include "ApplicationReputation.h" #include "nsUrlClassifierDBService.h" #include "nsUrlClassifierStreamUpdater.h" #include "nsUrlClassifierUtils.h" #include "nsUrlClassifierPrefixSet.h" -#endif #include "nsBrowserStatusFilter.h" #include "mozilla/FinalizationWitnessService.h" #include "mozilla/NativeOSFileInternals.h" #include "mozilla/AddonContentPolicy.h" #include "mozilla/AddonPathService.h" #if defined(XP_WIN) @@ -86,17 +84,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsAlertsS NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDownloadManager, nsDownloadManager::GetSingleton) NS_GENERIC_FACTORY_CONSTRUCTOR(DownloadPlatform) NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadProxy) NS_GENERIC_FACTORY_CONSTRUCTOR(nsTypeAheadFind) -#ifdef MOZ_URL_CLASSIFIER NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ApplicationReputationService, ApplicationReputationService::GetSingleton) NS_GENERIC_FACTORY_CONSTRUCTOR(nsUrlClassifierPrefixSet) NS_GENERIC_FACTORY_CONSTRUCTOR(nsUrlClassifierStreamUpdater) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUrlClassifierUtils, Init) static nsresult nsUrlClassifierDBServiceConstructor(nsISupports *aOuter, REFNSIID aIID, @@ -111,17 +108,16 @@ nsUrlClassifierDBServiceConstructor(nsIS return rv; } /* NS_ADDREF(inst); */ rv = inst->QueryInterface(aIID, aResult); NS_RELEASE(inst); return rv; } -#endif NS_GENERIC_FACTORY_CONSTRUCTOR(nsBrowserStatusFilter) #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateProcessor) #endif NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(FinalizationWitnessService, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init) @@ -143,23 +139,21 @@ NS_DEFINE_NAMED_CID(NS_ALERTSSERVICE_CID #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS) NS_DEFINE_NAMED_CID(NS_PARENTALCONTROLSSERVICE_CID); #endif NS_DEFINE_NAMED_CID(NS_DOWNLOADMANAGER_CID); NS_DEFINE_NAMED_CID(NS_DOWNLOADPLATFORM_CID); NS_DEFINE_NAMED_CID(NS_DOWNLOAD_CID); NS_DEFINE_NAMED_CID(NS_FIND_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_TYPEAHEADFIND_CID); -#ifdef MOZ_URL_CLASSIFIER NS_DEFINE_NAMED_CID(NS_APPLICATION_REPUTATION_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERPREFIXSET_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERDBSERVICE_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERSTREAMUPDATER_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERUTILS_CID); -#endif NS_DEFINE_NAMED_CID(NS_BROWSERSTATUSFILTER_CID); #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID); #endif NS_DEFINE_NAMED_CID(FINALIZATIONWITNESSSERVICE_CID); NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERNALS_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_ADDONCONTENTPOLICY_CID); NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID); @@ -179,23 +173,21 @@ static const Module::CIDEntry kToolkitCI #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS) { &kNS_PARENTALCONTROLSSERVICE_CID, false, nullptr, nsParentalControlsServiceConstructor }, #endif { &kNS_DOWNLOADMANAGER_CID, false, nullptr, nsDownloadManagerConstructor }, { &kNS_DOWNLOADPLATFORM_CID, false, nullptr, DownloadPlatformConstructor }, { &kNS_DOWNLOAD_CID, false, nullptr, nsDownloadProxyConstructor }, { &kNS_FIND_SERVICE_CID, false, nullptr, nsFindServiceConstructor }, { &kNS_TYPEAHEADFIND_CID, false, nullptr, nsTypeAheadFindConstructor }, -#ifdef MOZ_URL_CLASSIFIER { &kNS_APPLICATION_REPUTATION_SERVICE_CID, false, nullptr, ApplicationReputationServiceConstructor }, { &kNS_URLCLASSIFIERPREFIXSET_CID, false, nullptr, nsUrlClassifierPrefixSetConstructor }, { &kNS_URLCLASSIFIERDBSERVICE_CID, false, nullptr, nsUrlClassifierDBServiceConstructor }, { &kNS_URLCLASSIFIERSTREAMUPDATER_CID, false, nullptr, nsUrlClassifierStreamUpdaterConstructor }, { &kNS_URLCLASSIFIERUTILS_CID, false, nullptr, nsUrlClassifierUtilsConstructor }, -#endif { &kNS_BROWSERSTATUSFILTER_CID, false, nullptr, nsBrowserStatusFilterConstructor }, #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) { &kNS_UPDATEPROCESSOR_CID, false, nullptr, nsUpdateProcessorConstructor }, #endif { &kFINALIZATIONWITNESSSERVICE_CID, false, nullptr, FinalizationWitnessServiceConstructor }, { &kNATIVE_OSFILE_INTERNALS_SERVICE_CID, false, nullptr, NativeOSFileInternalsServiceConstructor }, { &kNS_ADDONCONTENTPOLICY_CID, false, nullptr, AddonContentPolicyConstructor }, { &kNS_ADDON_PATH_SERVICE_CID, false, nullptr, AddonPathServiceConstructor }, @@ -216,24 +208,22 @@ static const Module::ContractIDEntry kTo { NS_ALERTSERVICE_CONTRACTID, &kNS_ALERTSSERVICE_CID }, #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS) { NS_PARENTALCONTROLSSERVICE_CONTRACTID, &kNS_PARENTALCONTROLSSERVICE_CID }, #endif { NS_DOWNLOADMANAGER_CONTRACTID, &kNS_DOWNLOADMANAGER_CID }, { NS_DOWNLOADPLATFORM_CONTRACTID, &kNS_DOWNLOADPLATFORM_CID }, { NS_FIND_SERVICE_CONTRACTID, &kNS_FIND_SERVICE_CID }, { NS_TYPEAHEADFIND_CONTRACTID, &kNS_TYPEAHEADFIND_CID }, -#ifdef MOZ_URL_CLASSIFIER { NS_APPLICATION_REPUTATION_SERVICE_CONTRACTID, &kNS_APPLICATION_REPUTATION_SERVICE_CID }, { NS_URLCLASSIFIERPREFIXSET_CONTRACTID, &kNS_URLCLASSIFIERPREFIXSET_CID }, { NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID }, { NS_URICLASSIFIERSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID }, { NS_URLCLASSIFIERSTREAMUPDATER_CONTRACTID, &kNS_URLCLASSIFIERSTREAMUPDATER_CID }, { NS_URLCLASSIFIERUTILS_CONTRACTID, &kNS_URLCLASSIFIERUTILS_CID }, -#endif { NS_BROWSERSTATUSFILTER_CONTRACTID, &kNS_BROWSERSTATUSFILTER_CID }, #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) { NS_UPDATEPROCESSOR_CONTRACTID, &kNS_UPDATEPROCESSOR_CID }, #endif { FINALIZATIONWITNESSSERVICE_CONTRACTID, &kFINALIZATIONWITNESSSERVICE_CID }, { NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID, &kNATIVE_OSFILE_INTERNALS_SERVICE_CID }, { NS_ADDONCONTENTPOLICY_CONTRACTID, &kNS_ADDONCONTENTPOLICY_CID }, { NS_ADDONPATHSERVICE_CONTRACTID, &kNS_ADDON_PATH_SERVICE_CID },
--- a/toolkit/components/downloads/moz.build +++ b/toolkit/components/downloads/moz.build @@ -27,30 +27,26 @@ XPIDL_SOURCES += [ 'nsIDownloadManager.idl', 'nsIDownloadManagerUI.idl', 'nsIDownloadProgressListener.idl', ] XPIDL_MODULE = 'downloads' UNIFIED_SOURCES += [ - 'nsDownloadManager.cpp', + 'ApplicationReputation.cpp', + 'chromium/chrome/common/safe_browsing/csd.pb.cc', + 'nsDownloadManager.cpp' ] # SQLFunctions.cpp cannot be built in unified mode because of Windows headers. SOURCES += [ 'SQLFunctions.cpp', ] -if CONFIG['MOZ_URL_CLASSIFIER']: - UNIFIED_SOURCES += [ - 'ApplicationReputation.cpp', - 'chromium/chrome/common/safe_browsing/csd.pb.cc' - ] - if CONFIG['OS_ARCH'] == 'WINNT': # Can't build unified because we need CreateEvent which some IPC code # included in LoadContext ends up undefining. SOURCES += [ 'nsDownloadScanner.cpp', ] # XXX - Until Suite builds off XULRunner we can't guarantee our implementation
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_background_debug_global.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_background_debug_global.html @@ -24,29 +24,28 @@ const { /** * This test is asserting that ext-backgroundPage.js successfully sets its * debug global in the AddonWrapper provided by XPIProvider.jsm * * It does _not_ test any functionality in devtools and does not guarantee * debugging is actually working correctly end-to-end. */ -function backgroundScript() { +function background() { window.testThing = "test!"; browser.test.notifyPass("background script ran"); } const ID = "debug@tests.mozilla.org"; let extensionData = { useAddonManager: "temporary", - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { applications: {gecko: {id: ID}}, }, - files: {}, }; add_task(function* () { let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); yield extension.awaitFinish("background script ran");
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_background_page.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_background_page.html @@ -21,17 +21,17 @@ Cu.import("resource://testing-common/Tes add_task(function* testAlertNotShownInBackgroundWindow() { ok(!Services.wm.getEnumerator("alert:alert").hasMoreElements(), "Alerts should not be present at the start of the test."); let consoleOpened = TestUtils.topicObserved("web-console-created"); let extension = ExtensionTestUtils.loadExtension({ - background: "new " + function() { + background: function() { browser.test.log("background script executed"); alert("I am an alert in the background."); browser.test.notifyPass("alertCalled"); }, });
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_contentscript_unrecognizedprop_warning.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_contentscript_unrecognizedprop_warning.html @@ -12,17 +12,17 @@ <body> <script type="text/javascript"> "use strict"; const BASE = "http://mochi.test:8888/chrome/toolkit/components/extensions/test/mochitest"; add_task(function* test_contentscript() { - function backgroundScript() { + function background() { browser.runtime.onMessage.addListener((msg) => { if (msg == "loaded") { browser.tabs.query({active: true, currentWindow: true}).then((tabs) => { // NOTE: we're removing the tab from here because doing a win.close() // from the chrome test code is raising a "TypeError: can 't access // dead object" exception. browser.tabs.remove(tabs[0].id); @@ -42,20 +42,20 @@ add_task(function* test_contentscript() { "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_idle", "unrecognized_property": "with-a-random-value", }, ], }, - background: "(" + backgroundScript.toString() + ")()", + background, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); SimpleTest.waitForExplicitFinish(); let waitForConsole = new Promise(resolve => { SimpleTest.monitorConsole(resolve, [{
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_eventpage_warning.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_eventpage_warning.html @@ -20,20 +20,20 @@ function createEventPageExtension(eventP browser.test.sendMessage("running", 1); } return ExtensionTestUtils.loadExtension({ manifest: { "background": eventPage, }, files: { - "event-page-script.js": `(${eventPageScript})()`, + "event-page-script.js": eventPageScript, "event-page.html": `<html><head> <meta charset="utf-8"> - <script src="event-page-script.js"></${"script"}> + <script src="event-page-script.js"><\/script> </head></html>`, }, }); } add_task(function* test_eventpages() { // Used in other tests to prevent the monitorConsole to grip. SimpleTest.waitForExplicitFinish();
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_shutdown_cleanup.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_shutdown_cleanup.html @@ -22,17 +22,17 @@ const {GlobalManager} = Cu.import("resou /* eslint-disable mozilla/balanced-listeners */ add_task(function* testShutdownCleanup() { is(GlobalManager.initialized, false, "GlobalManager start as not initialized"); let extension = ExtensionTestUtils.loadExtension({ - background: "new " + function() { + background: function() { browser.test.notifyPass("background page loaded"); }, }); yield extension.startup(); yield extension.awaitFinish("background page loaded");
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_storage_cleanup.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_storage_cleanup.html @@ -101,17 +101,17 @@ add_task(function* test_uninstall() { yield SpecialPowers.pushPrefEnv({ set: [["extensions.webextensions.keepUuidOnUninstall", true]], }); yield SpecialPowers.pushPrefEnv({ set: [["extensions.webextensions.keepStorageOnUninstall", true]], }); let extension = ExtensionTestUtils.loadExtension({ - background: `(${writeData})()`, + background: writeData, manifest: { applications: {gecko: {id: ID}}, permissions: ["storage"], }, useAddonManager: "temporary", }); yield extension.startup(); @@ -120,17 +120,17 @@ add_task(function* test_uninstall() { // Check that we can still see data we wrote to storage but clear the // "leave storage" flag so our storaged gets cleared on uninstall. // This effectively tests the keepUuidOnUninstall logic, which ensures // that when we read storage again and check that it is cleared, that // it is actually a meaningful test! yield SpecialPowers.popPrefEnv(); extension = ExtensionTestUtils.loadExtension({ - background: `(${readData})()`, + background: readData, manifest: { applications: {gecko: {id: ID}}, permissions: ["storage"], }, useAddonManager: "temporary", }); yield extension.startup(); @@ -138,17 +138,17 @@ add_task(function* test_uninstall() { is(results.matchLocalStorage, true, "localStorage data is still present"); is(results.matchIDB, true, "indexedDB data is still present"); is(results.matchBrowserStorage, true, "browser.storage.local data is still present"); yield extension.unload(); // Read again. This time, our data should be gone. extension = ExtensionTestUtils.loadExtension({ - background: `(${readData})()`, + background: readData, manifest: { applications: {gecko: {id: ID}}, permissions: ["storage"], }, useAddonManager: "temporary", }); yield extension.startup();
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_trustworthy_origin.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_trustworthy_origin.html @@ -20,23 +20,22 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager", "@mozilla.org/contentsecuritymanager;1", "nsIContentSecurityManager"); add_task(function* () { - function backgroundScript() { + function background() { browser.test.sendMessage("ready", browser.runtime.getURL("/test.html")); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", - manifest: {}, + background, files: { "test.html": `<html><head></head><body></body></html>`, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup();
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_webnavigation_resolved_urls.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_webnavigation_resolved_urls.html @@ -10,17 +10,17 @@ <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; add_task(function* webnav_unresolved_uri_on_expected_URI_scheme() { - function backgroundScript() { + function background() { let checkURLs; browser.webNavigation.onCompleted.addListener((msg) => { if (checkURLs.length > 0) { let expectedURL = checkURLs.shift(); browser.test.assertEq(expectedURL, msg.url, "Got the expected URL"); browser.tabs.remove(msg.tabId).then(() => { browser.test.sendMessage("next"); @@ -38,17 +38,17 @@ add_task(function* webnav_unresolved_uri } let extensionData = { manifest: { permissions: [ "webNavigation", ], }, - background: "new " + backgroundScript, + background, files: { "tab.html": `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> </html> `,
--- a/toolkit/components/extensions/test/mochitest/test_ext_background_api_injection.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_background_api_injection.html @@ -10,17 +10,17 @@ </head> <body> <script type="text/javascript"> "use strict"; add_task(function* testBackgroundWindow() { let extension = ExtensionTestUtils.loadExtension({ - background: "new " + function() { + background: function() { const BASE = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest"; browser.test.log("background script executed"); window.location = `${BASE}/file_privilege_escalation.html`; }, }); let awaitConsole = new Promise(resolve => {
--- a/toolkit/components/extensions/test/mochitest/test_ext_background_canvas.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_background_canvas.html @@ -27,17 +27,17 @@ add_task(function* test_background_canva browser.test.notifyPass("background-canvas"); } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFail("background-canvas"); } } let extensionData = { - background: `(${background})()`, + background, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); yield extension.awaitFinish("background-canvas"); yield extension.unload(); });
--- a/toolkit/components/extensions/test/mochitest/test_ext_background_generated_url.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_background_generated_url.html @@ -22,17 +22,17 @@ add_task(function* test_url_of_generated let extension = ExtensionTestUtils.loadExtension({ manifest: { background: { scripts: ["bg.js"], }, web_accessible_resources: ["_generated_background_page.html"], }, files: { - "bg.js": `(${backgroundScript})();`, + "bg.js": backgroundScript, }, }); yield extension.startup(); const EXPECTED_URL = yield extension.awaitMessage("script done"); let win = window.open(EXPECTED_URL); ok(win, "Should open new tab at URL: " + EXPECTED_URL);
--- a/toolkit/components/extensions/test/mochitest/test_ext_background_teardown.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_background_teardown.html @@ -9,30 +9,30 @@ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> </head> <body> <script> "use strict"; add_task(function* test_background_reload_and_unload() { - function backgroundScript() { + function background() { browser.test.onMessage.addListener(msg => { browser.test.assertEq("reload-background", msg); location.reload(); }); browser.test.sendMessage("background-url", location.href); } let chromeScript = SpecialPowers.loadChromeScript( SimpleTest.getTestFileURL("file_teardown_test.js")); yield chromeScript.promiseOneMessage("chromescript-startup"); let extensionData = { - background: `(${backgroundScript})();`, + background, }; let extension = ExtensionTestUtils.loadExtension(extensionData); function* getContextEvents() { chromeScript.sendAsyncMessage("get-context-events"); let contextEvents = yield chromeScript.promiseOneMessage("context-events"); return contextEvents.filter(event => event.extensionId == extension.id); }
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html @@ -9,17 +9,17 @@ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; add_task(function* test_contentscript() { - function backgroundScript() { + function background() { browser.runtime.onMessage.addListener(([msg, expectedState, readyState], sender) => { if (msg == "chrome-namespace-ok") { browser.test.sendMessage(msg); return; } browser.test.assertEq(msg, "script-run", "message type is correct"); browser.test.assertEq(readyState, expectedState, "readyState is correct"); @@ -64,23 +64,23 @@ add_task(function* test_contentscript() }, { "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_idle", }, ], }, - background: "(" + backgroundScript.toString() + ")()", + background, files: { - "content_script_start.js": "(" + contentScriptStart.toString() + ")()", - "content_script_end.js": "(" + contentScriptEnd.toString() + ")()", - "content_script_idle.js": "(" + contentScriptIdle.toString() + ")()", - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script_start.js": contentScriptStart, + "content_script_end.js": contentScriptEnd, + "content_script_idle.js": contentScriptIdle, + "content_script.js": contentScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); let loadingCount = 0; let interactiveCount = 0; let completeCount = 0;
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_api_injection.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_api_injection.html @@ -47,18 +47,18 @@ add_task(function* test_contentscript_ap }, ], "web_accessible_resources": [ "content_script_iframe.html", ], }, files: { - "content_script.js": "new " + contentScript, - "content_script_iframe.js": "new " + contentScriptIframe, + "content_script.js": contentScript, + "content_script_iframe.js": contentScriptIframe, "content_script_iframe.html": document.querySelector("#test-asset").textContent, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); let awaitConsole = new Promise(resolve => { let chromeScript = SpecialPowers.loadChromeScript(
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_context.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_context.html @@ -31,17 +31,17 @@ add_task(function* test_contentscript_co manifest: { content_scripts: [{ "matches": ["http://example.com/"], "js": ["content_script.js"], }], }, files: { - "content_script.js": `(${contentScript})()`, + "content_script.js": contentScript, }, }); yield extension.startup(); let win = window.open("http://example.com/"); yield extension.awaitMessage("content-script-ready"); yield extension.awaitMessage("content-script-show");
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_create_iframe.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_create_iframe.html @@ -20,17 +20,17 @@ </head> </html> </textarea> <script type="text/javascript"> "use strict"; add_task(function* test_contentscript_create_iframe() { - function backgroundScript() { + function background() { browser.runtime.onMessage.addListener((msg, sender) => { let {name, availableAPIs, manifest, testGetManifest} = msg; let hasExtTabsAPI = availableAPIs.indexOf("tabs") > 0; let hasExtWindowsAPI = availableAPIs.indexOf("windows") > 0; browser.test.assertFalse(hasExtTabsAPI, "the created iframe should not be able to use privileged APIs (tabs)"); browser.test.assertFalse(hasExtWindowsAPI, "the created iframe should not be able to use privileged APIs (windows)"); @@ -92,22 +92,22 @@ add_task(function* test_contentscript_cr "run_at": "document_idle", }, ], web_accessible_resources: [ "content_script_iframe.html", ], }, - background: "(" + backgroundScript + ")()", + background, files: { - "content_script.js": "new " + contentScript, + "content_script.js": contentScript, "content_script_iframe.html": document.querySelector("#test-asset").textContent, - "content_script_iframe.js": "new " + contentScriptIframe, + "content_script_iframe.js": contentScriptIframe, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); let contentScriptIframeCreatedPromise = new Promise(resolve => { extension.onMessage("content-script-iframe-loaded", () => { resolve(); }); });
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_devtools_metadata.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_devtools_metadata.html @@ -13,17 +13,17 @@ <script type="text/javascript"> "use strict"; add_task(function* test_contentscript_devtools_sandbox_metadata() { function contentScript() { browser.runtime.sendMessage("contentScript.executed"); } - function backgroundScript() { + function background() { browser.runtime.onMessage.addListener((msg) => { if (msg == "contentScript.executed") { browser.test.notifyPass("contentScript.executed"); } }); } let extensionData = { @@ -32,19 +32,19 @@ add_task(function* test_contentscript_de { "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_idle", }, ], }, - background: "new " + backgroundScript, + background, files: { - "content_script.js": "new " + contentScript, + "content_script.js": contentScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_exporthelpers.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_exporthelpers.html @@ -69,17 +69,17 @@ add_task(function* test_contentscript_ex content_scripts: [{ js: ["contentscript.js"], matches: ["http://mochi.test/*/file_sample.html"], run_at: "document_start", }], }, files: { - "contentscript.js": `(${contentScript})();`, + "contentscript.js": contentScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_teardown.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_teardown.html @@ -12,36 +12,36 @@ <script> "use strict"; add_task(function* test_contentscript_reload_and_unload() { function contentScript() { browser.test.sendMessage("contentscript-run"); } - function backgroundScript() { + function background() { let removedTabs = 0; browser.tabs.onRemoved.addListener(() => { browser.test.assertEq(1, ++removedTabs, "Expected only one tab to be removed during the test"); browser.test.sendMessage("tab-closed"); }); } let extensionData = { - background: `(${backgroundScript})();`, + background, manifest: { content_scripts: [{ "matches": ["http://mochi.test/*/file_sample.html"], "js": ["contentscript.js"], }], }, files: { - "contentscript.js": `(${contentScript})();`, + "contentscript.js": contentScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let chromeScript = SpecialPowers.loadChromeScript( SimpleTest.getTestFileURL("file_teardown_test.js"));
--- a/toolkit/components/extensions/test/mochitest/test_ext_cookies.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_cookies.html @@ -9,17 +9,17 @@ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; add_task(function* test_cookies() { - function backgroundScript() { + function background() { function assertExpected(expected, cookie) { for (let key of Object.keys(cookie)) { browser.test.assertTrue(key in expected, `found property ${key}`); browser.test.assertEq(expected[key], cookie[key], `property value for ${key} is correct`); } browser.test.assertEq(Object.keys(expected).length, Object.keys(cookie).length, "all expected properties found"); } @@ -163,17 +163,17 @@ add_task(function* test_cookies() { browser.test.assertEq("", cookie.name, "default name set"); browser.test.assertEq("", cookie.value, "default value set"); browser.test.assertEq(true, cookie.session, "no expiry date created session cookie"); browser.test.notifyPass("cookies"); }); } let extension = ExtensionTestUtils.loadExtension({ - background: `(${backgroundScript})()`, + background, manifest: { permissions: ["cookies", "*://example.org/"], }, }); yield extension.startup(); info("extension loaded"); yield extension.awaitFinish("cookies");
--- a/toolkit/components/extensions/test/mochitest/test_ext_cookies_expiry.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_cookies_expiry.html @@ -37,17 +37,17 @@ add_task(function* test_cookies_expiry() }, 1000); } let domain = ".example.com"; let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["http://example.com/", "cookies"], }, - background: `(${background})()`, + background, }); let cookieSvc = SpecialPowers.Services.cookies; let cookie = { host: domain, name: "first", path: "/",
--- a/toolkit/components/extensions/test/mochitest/test_ext_exclude_include_globs.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_exclude_include_globs.html @@ -9,17 +9,17 @@ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; add_task(function* test_contentscript() { - function backgroundScript() { + function background() { browser.runtime.onMessage.addListener(([script], sender) => { browser.test.sendMessage("run", {script}); browser.test.sendMessage("run-" + script); }); browser.test.sendMessage("running"); } function contentScriptAll() { @@ -48,22 +48,22 @@ add_task(function* test_contentscript() }, { "matches": ["http://example.org/", "http://*.example.org/"], "exclude_globs": ["*test1*"], "js": ["content_script_excludes_test1.js"], }, ], }, - background: "(" + backgroundScript.toString() + ")()", + background, files: { - "content_script_all.js": "(" + contentScriptAll.toString() + ")()", - "content_script_includes_test1.js": "(" + contentScriptIncludesTest1.toString() + ")()", - "content_script_excludes_test1.js": "(" + contentScriptExcludesTest1.toString() + ")()", + "content_script_all.js": contentScriptAll, + "content_script_includes_test1.js": contentScriptIncludesTest1, + "content_script_excludes_test1.js": contentScriptExcludesTest1, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); let ran = 0; extension.onMessage("run", ({script}) => {
--- a/toolkit/components/extensions/test/mochitest/test_ext_generate.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_generate.html @@ -8,31 +8,31 @@ <script type="text/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; -function backgroundScript() { +function background() { browser.test.log("running background script"); browser.test.onMessage.addListener((x, y) => { browser.test.assertEq(x, 10, "x is 10"); browser.test.assertEq(y, 20, "y is 20"); browser.test.notifyPass("background test passed"); }); browser.test.sendMessage("running", 1); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", + background, }; add_task(function* test_background() { let extension = ExtensionTestUtils.loadExtension(extensionData); info("load complete"); let [, x] = yield Promise.all([extension.startup(), extension.awaitMessage("running")]); is(x, 1, "got correct value from extension"); info("startup complete");
--- a/toolkit/components/extensions/test/mochitest/test_ext_geturl.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_geturl.html @@ -8,17 +8,17 @@ <script type="text/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; -function backgroundScript() { +function background() { browser.runtime.onMessage.addListener(([url1, url2]) => { let url3 = browser.runtime.getURL("test_file.html"); let url4 = browser.extension.getURL("test_file.html"); browser.test.assertTrue(url1 !== undefined, "url1 defined"); browser.test.assertTrue(url1.startsWith("moz-extension://"), "url1 has correct scheme"); browser.test.assertTrue(url1.endsWith("test_file.html"), "url1 has correct leaf name"); @@ -33,27 +33,27 @@ function backgroundScript() { function contentScript() { let url1 = browser.runtime.getURL("test_file.html"); let url2 = browser.extension.getURL("test_file.html"); browser.runtime.sendMessage([url1, url2]); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_idle", }], }, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_i18n.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_i18n.html @@ -208,20 +208,20 @@ add_task(function* test_get_accept_langu manifest: { "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "run_at": "document_start", "js": ["content_script.js"], }], }, - background: `(${background})()`, + background, files: { - "content_script.js": `(${content})()`, + "content_script.js": content, }, }); let win = window.open("file_sample.html"); yield extension.startup(); yield extension.awaitMessage("ready"); @@ -326,17 +326,17 @@ add_task(function* test_detect_language( "of winkels nie en slegs oornagbesoekers word toegelaat bateleur"; // String with intermixed French/English text const fr_en_string = "France is the largest country in Western Europe and the third-largest in Europe as a whole. " + "A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections et exporter " + "Cet article concerne le pays européen aujourd’hui appelé République française. Pour d’autres usages du nom France, " + "Pour une aide rapide et effective, veuiller trouver votre aide dans le menu ci-dessus." + "Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. The quick brown fox jumped over the lazy dog"; - function backgroundScript() { + function background() { function checkResult(source, result, expected) { browser.test.assertEq(expected.isReliable, result.isReliable, "result.confident is true"); browser.test.assertEq( expected.languages.length, result.languages.length, `result.languages contains the expected number of languages in ${source}`); expected.languages.forEach((lang, index) => { browser.test.assertEq( @@ -382,20 +382,20 @@ add_task(function* test_detect_language( manifest: { "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "run_at": "document_start", "js": ["content_script.js"], }], }, - background: `(${backgroundScript})()`, + background, files: { - "content_script.js": `(${content})()`, + "content_script.js": content, }, }); let win = window.open("file_sample.html"); yield extension.startup(); yield extension.awaitMessage("ready");
--- a/toolkit/components/extensions/test/mochitest/test_ext_i18n_css.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_i18n_css.html @@ -10,17 +10,17 @@ </head> <body> <script type="text/javascript"> "use strict"; add_task(function* test_i18n_css() { let extension = ExtensionTestUtils.loadExtension({ - background: "new " + function() { + background: function() { function fetch(url) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = () => { resolve(xhr.responseText); }; xhr.onerror = reject; xhr.send(); });
--- a/toolkit/components/extensions/test/mochitest/test_ext_inIncognitoContext_window.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_inIncognitoContext_window.html @@ -23,23 +23,22 @@ add_task(function* test_in_incognito_con browser.windows.create({url: browser.runtime.getURL("/tab.html"), incognito: true}); } function tabScript() { browser.runtime.sendMessage(browser.extension.inIncognitoContext); } let extension = ExtensionTestUtils.loadExtension({ - background: `(${background})()`, - manifest: {}, + background, files: { - "tab.js": `(${tabScript})()`, + "tab.js": tabScript, "tab.html": `<!DOCTYPE html><html><head> <meta charset="utf-8"> - <script src="tab.js"></${"script"}> + <script src="tab.js"><\/script> </head></html>`, }, }); yield extension.startup(); yield extension.awaitFinish("inIncognitoContext"); yield extension.unload(); });
--- a/toolkit/components/extensions/test/mochitest/test_ext_jsversion.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_jsversion.html @@ -13,49 +13,49 @@ <body> <script type="text/javascript"> "use strict"; add_task(function* test_versioned_js() { // We need to deal with escaping the close script tags. // May as well consolidate it into one place. - let script = attrs => `<script ${attrs}></${"script"}>`; + let script = attrs => `<script ${attrs}><\/script>`; let extension = ExtensionTestUtils.loadExtension({ manifest: { "background": {"page": "background.html"}, }, files: { "background.html": ` <meta charset="utf-8"> ${script('src="background.js" type="application/javascript"')} ${script('src="background-1.js" type="application/javascript;version=1.8"')} ${script('src="background-2.js" type="application/javascript;version=latest"')} ${script('src="background-3.js" type="application/javascript"')} `, - "background.js": "new " + function() { + "background.js": function() { window.reportResult = msg => { browser.test.assertEq( msg, "background-script-3", "Expected a message only from the unversioned background script."); browser.test.sendMessage("finished"); }; }, - "background-1.js": "new " + function() { + "background-1.js": function() { window.reportResult("background-script-1"); }, - "background-2.js": "new " + function() { + "background-2.js": function() { window.reportResult("background-script-2"); }, - "background-3.js": "new " + function() { + "background-3.js": function() { window.reportResult("background-script-3"); }, }, }); let messages = [/Versioned JavaScript.*not supported in WebExtension.*developer\.mozilla\.org/, /Versioned JavaScript.*not supported in WebExtension.*developer\.mozilla\.org/];
--- a/toolkit/components/extensions/test/mochitest/test_ext_notifications.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_notifications.html @@ -14,44 +14,44 @@ // A 1x1 PNG image. // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) let image = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; add_task(function* test_notification() { - function backgroundScript() { + function background() { let opts = { type: "basic", title: "Testing Notification", message: "Carry on", }; browser.notifications.create(opts).then(id => { browser.test.sendMessage("running", id); browser.test.notifyPass("background test passed"); }); } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["notifications"], }, - background: `(${backgroundScript})()`, + background, }); yield extension.startup(); let x = yield extension.awaitMessage("running"); is(x, "0", "got correct id from notifications.create"); yield extension.awaitFinish(); yield extension.unload(); }); add_task(function* test_notification_events() { - function backgroundScript() { + function background() { let opts = { type: "basic", title: "Testing Notification", message: "Carry on", }; // Test an ignored listener. browser.notifications.onButtonClicked.addListener(function() {}); @@ -72,29 +72,29 @@ add_task(function* test_notification_eve browser.test.sendMessage("running", id); }); } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["notifications"], }, - background: `(${backgroundScript})()`, + background, }); yield extension.startup(); let x = yield extension.awaitMessage("closed"); is(x, "5", "got correct id from onClosed listener"); x = yield extension.awaitMessage("running"); is(x, "5", "got correct id from notifications.create"); yield extension.awaitFinish(); yield extension.unload(); }); add_task(function* test_notification_clear() { - function backgroundScript() { + function background() { let opts = { type: "basic", title: "Testing Notification", message: "Carry on", }; browser.notifications.onClosed.addListener(id => { browser.test.sendMessage("closed", id); @@ -107,49 +107,49 @@ add_task(function* test_notification_cle browser.test.notifyPass("background test passed"); }); } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["notifications"], }, - background: `(${backgroundScript})()`, + background, }); yield extension.startup(); let x = yield extension.awaitMessage("closed"); is(x, "99", "got correct id from onClosed listener"); x = yield extension.awaitMessage("cleared"); is(x, true, "got correct boolean from notifications.clear"); yield extension.awaitFinish(); yield extension.unload(); }); add_task(function* test_notifications_empty_getAll() { - function backgroundScript() { + function background() { browser.notifications.getAll().then(notifications => { browser.test.assertEq("object", typeof notifications, "getAll() returned an object"); browser.test.assertEq(0, Object.keys(notifications).length, "the object has no properties"); browser.test.notifyPass("getAll empty"); }); } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["notifications"], }, - background: `(${backgroundScript})()`, + background, }); yield extension.startup(); yield extension.awaitFinish("getAll empty"); yield extension.unload(); }); add_task(function* test_notifications_populated_getAll() { - function backgroundScript() { + function background() { let opts = { type: "basic", iconUrl: "a.png", title: "Testing Notification", message: "Carry on", }; browser.notifications.create("p1", opts).then(() => { @@ -171,28 +171,28 @@ add_task(function* test_notifications_po browser.test.notifyPass("getAll populated"); }); } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["notifications"], }, - background: `(${backgroundScript})()`, + background, files: { "a.png": IMAGE_ARRAYBUFFER, }, }); yield extension.startup(); yield extension.awaitFinish("getAll populated"); yield extension.unload(); }); add_task(function* test_buttons_unsupported() { - function backgroundScript() { + function background() { let opts = { type: "basic", title: "Testing Notification", message: "Carry on", buttons: [{title: "Button title"}], }; let exception = {}; @@ -208,17 +208,17 @@ add_task(function* test_buttons_unsuppor ); browser.test.notifyPass("buttons-unsupported"); } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["notifications"], }, - background: `(${backgroundScript})()`, + background, }); yield extension.startup(); yield extension.awaitFinish("buttons-unsupported"); yield extension.unload(); }); </script>
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html @@ -8,17 +8,17 @@ <script type="text/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; -function backgroundScript() { +function background() { browser.runtime.onConnect.addListener(port => { browser.test.assertEq(port.name, "ernie", "port name correct"); browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "URL correct"); browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "tab URL correct"); let expected = "message 1"; port.onMessage.addListener(msg => { browser.test.assertEq(msg, expected, "message is expected"); @@ -43,28 +43,28 @@ function contentScript() { if (msg == "message 2") { port.postMessage("message 3"); port.disconnect(); } }); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "permissions": ["tabs"], "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_start", }], }, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_twoway.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_twoway.html @@ -9,17 +9,17 @@ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> </head> <body> <script> "use strict"; add_task(function* test_connect_bidirectionally_and_postMessage() { - function backgroundScript() { + function background() { let onConnectCount = 0; browser.runtime.onConnect.addListener(port => { // 3. onConnect by connect() from CS. browser.test.assertEq("from-cs", port.name); browser.test.assertEq(1, ++onConnectCount, "BG onConnect should be called once"); let tabId = port.sender.tab.id; @@ -93,25 +93,25 @@ add_task(function* test_connect_bidirect "CS port.onMessage should be called once"); // 10. should trigger port.onMessage in BG. port.postMessage("from CS to port"); }); } let extensionData = { - background: `(${backgroundScript})();`, + background, manifest: { content_scripts: [{ js: ["contentscript.js"], matches: ["http://mochi.test/*/file_sample.html"], }], }, files: { - "contentscript.js": `(${contentScript})();`, + "contentscript.js": contentScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); info("extension loaded"); yield extension.awaitMessage("ready");
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_disconnect.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_disconnect.html @@ -8,17 +8,17 @@ <script type="text/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; -function backgroundScript() { +function background() { browser.runtime.onConnect.addListener(port => { browser.test.assertEq(port.name, "ernie", "port name correct"); port.onDisconnect.addListener(() => { // Closing an already-disconnected port is a no-op. port.disconnect(); port.disconnect(); browser.test.sendMessage("disconnected"); }); @@ -26,28 +26,28 @@ function backgroundScript() { }); } function contentScript() { browser.runtime.connect({name: "ernie"}); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "permissions": ["tabs"], "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_idle", }], }, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_id.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_id.html @@ -31,20 +31,20 @@ add_task(function* test_runtime_id() { applications: {gecko: {id}}, "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "run_at": "document_start", "js": ["content_script.js"], }], }, - background: `(${background})()`, + background, files: { - "content_script.js": `(${content})()`, + "content_script.js": content, }, }); yield extension.startup(); let backgroundId = yield extension.awaitMessage("background-id"); is(backgroundId, id, "runtime.id from background script is correct"); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_sandbox_var.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_sandbox_var.html @@ -8,41 +8,41 @@ <script type="text/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; -function backgroundScript() { +function background() { browser.runtime.onMessage.addListener(result => { browser.test.assertEq(result, 12, "x is 12"); browser.test.notifyPass("background test passed"); }); } function contentScript() { window.x = 12; browser.runtime.onMessage.addListener(function() {}); browser.runtime.sendMessage(window.x); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_idle", }], }, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_schema.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_schema.html @@ -18,17 +18,17 @@ add_task(function* testEmptySchema() { function background() { browser.test.assertTrue(!("manifest" in browser), "browser.manifest is not defined"); browser.test.assertTrue("storage" in browser, "browser.storage should be defined"); browser.test.assertTrue(!("contextMenus" in browser), "browser.contextMenus should not be defined"); browser.test.notifyPass("schema"); } let extension = ExtensionTestUtils.loadExtension({ - background: `(${background})()`, + background, manifest: { permissions: ["storage"], }, }); yield extension.startup(); yield extension.awaitFinish("schema"); yield extension.unload(); @@ -41,17 +41,17 @@ add_task(function* testUnknownProperties let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["unknownPermission"], unknown_property: {}, }, - background: `(${background})()`, + background, }); let messages = [ {message: /processing permissions\.0: Unknown permission "unknownPermission"/}, {message: /processing unknown_property: An unexpected property was found in the WebExtension manifest/}, ]; let waitForConsole = new Promise(resolve => {
--- a/toolkit/components/extensions/test/mochitest/test_ext_sendmessage_doublereply.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_sendmessage_doublereply.html @@ -8,17 +8,17 @@ <script type="text/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; -function backgroundScript() { +function background() { // Add two listeners that both send replies. We're supposed to ignore all but one // of them. Which one is chosen is non-deterministic. browser.runtime.onMessage.addListener((msg, sender, sendReply) => { browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct"); if (msg == "getreply") { sendReply("reply1"); @@ -62,28 +62,28 @@ function contentScript() { if (resp != "reply1" && resp != "reply2") { return; // test failed } browser.runtime.sendMessage("done"); }); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "permissions": ["tabs"], "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_start", }], }, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_sendmessage_no_receiver.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_sendmessage_no_receiver.html @@ -17,17 +17,17 @@ function loadContentScriptExtension(cont let extensionData = { manifest: { "content_scripts": [{ "js": ["contentscript.js"], "matches": ["http://mochi.test/*/file_sample.html"], }], }, files: { - "contentscript.js": `(${contentScript})();`, + "contentscript.js": contentScript, }, }; return ExtensionTestUtils.loadExtension(extensionData); } add_task(function* test_content_script_sendMessage_without_listener() { function contentScript() { browser.runtime.sendMessage("msg").then(reply => {
--- a/toolkit/components/extensions/test/mochitest/test_ext_sendmessage_reply.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_sendmessage_reply.html @@ -8,17 +8,17 @@ <script type="text/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; -function backgroundScript() { +function background() { browser.runtime.onMessage.addListener((msg, sender, sendReply) => { browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct"); if (msg == 0) { sendReply("reply1"); } else if (msg == 1) { window.setTimeout(function() { sendReply("reply2"); @@ -40,28 +40,28 @@ function contentScript() { return; // test failed } browser.runtime.sendMessage(2); }); }); } let extensionData = { - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "permissions": ["tabs"], "content_scripts": [{ "matches": ["http://mochi.test/*/file_sample.html"], "js": ["content_script.js"], "run_at": "document_idle", }], }, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("file_sample.html");
--- a/toolkit/components/extensions/test/mochitest/test_ext_storage_content.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_storage_content.html @@ -174,17 +174,17 @@ let extensionData = { "js": ["content_script.js"], "run_at": "document_idle", }], permissions: ["storage"], }, files: { - "content_script.js": "(" + contentScript.toString() + ")()", + "content_script.js": contentScript, }, }; add_task(function* test_contentscript() { let win = window.open("file_sample.html"); yield waitForLoad(win); let extension = ExtensionTestUtils.loadExtension(extensionData);
--- a/toolkit/components/extensions/test/mochitest/test_ext_storage_tab.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_storage_tab.html @@ -81,28 +81,28 @@ add_task(function* test_multiple_pages() return browser.storage.local.set({key: {foo: {bar: "baz"}}}); } }); browser.runtime.sendMessage("tab-ready"); } let extension = ExtensionTestUtils.loadExtension({ - background: `(${background})()`, + background, files: { "tab.html": `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> - <script src="tab.js"></${"script"}> + <script src="tab.js"><\/script> </head> </html>`, - "tab.js": `(${tab})()`, + "tab.js": tab, }, manifest: { permissions: ["storage"], }, }); yield extension.startup();
--- a/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html @@ -9,17 +9,17 @@ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; add_task(function* test_webext_tab_subframe_privileges() { - function backgroundScript() { + function background() { browser.runtime.onMessage.addListener(({msg, success, tabId, error}) => { if (msg == "webext-tab-subframe-privileges") { if (success) { browser.tabs.remove(tabId) .then(() => browser.test.notifyPass(msg)); } else { browser.test.log(`Got an unexpected error: ${error}`); browser.tabs.query({active: true}) @@ -52,33 +52,33 @@ add_task(function* test_webext_tab_subfr msg: "webext-tab-subframe-privileges", success: false, error: `Privileged APIs missing in WebExtension tab sub-frame`, }); } } let extensionData = { - background: "new " + backgroundScript, + background, files: { "tab.html": `<!DOCTYPE> <head> <meta charset="utf-8"> </head> <body> <iframe src="tab-subframe.html"></iframe> </body> </html>`, "tab-subframe.html": `<!DOCTYPE> <head> <meta charset="utf-8"> - <script src="tab-subframe.js"></${"script"}> + <script src="tab-subframe.js"><\/script> </head> </html>`, - "tab-subframe.js": `(${tabSubframeScript})()`, + "tab-subframe.js": tabSubframeScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); yield extension.awaitFinish("webext-tab-subframe-privileges"); yield extension.unload(); @@ -104,32 +104,32 @@ add_task(function* test_webext_backgroun </head> <body> <iframe src="background-subframe.html"></iframe> </body> </html>`, "background-subframe.html": `<!DOCTYPE> <head> <meta charset="utf-8"> - <script src="background-subframe.js"></${"script"}> + <script src="background-subframe.js"><\/script> </head> </html>`, - "background-subframe.js": `(${backgroundSubframeScript})()`, + "background-subframe.js": backgroundSubframeScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); yield extension.awaitFinish("webext-background-subframe-privileges"); yield extension.unload(); }); add_task(function* test_webext_contentscript_iframe_subframe_privileges() { - function backgroundScript() { + function background() { browser.runtime.onMessage.addListener(({name, hasTabsAPI, hasStorageAPI}) => { if (name == "contentscript-iframe-loaded") { browser.test.assertFalse(hasTabsAPI, "Subframe of a content script privileged iframes has no access to privileged APIs"); browser.test.assertTrue(hasStorageAPI, "Subframe of a content script privileged iframes has access to content script APIs"); browser.test.notifyPass("webext-contentscript-subframe-privileges"); @@ -147,44 +147,44 @@ add_task(function* test_webext_contentsc function contentScript() { let iframe = document.createElement("iframe"); iframe.setAttribute("src", browser.runtime.getURL("/contentscript-iframe.html")); document.body.appendChild(iframe); } let extensionData = { - background: "new " + backgroundScript, + background, manifest: { "permissions": ["storage"], "content_scripts": [{ "matches": ["http://example.com/*"], "js": ["contentscript.js"], }], web_accessible_resources: [ "contentscript-iframe.html", ], }, files: { - "contentscript.js": `(${contentScript})()`, + "contentscript.js": contentScript, "contentscript-iframe.html": `<!DOCTYPE> <head> <meta charset="utf-8"> </head> <body> <iframe src="contentscript-iframe-subframe.html"></iframe> </body> </html>`, "contentscript-iframe-subframe.html": `<!DOCTYPE> <head> <meta charset="utf-8"> - <script src="contentscript-iframe-subframe.js"></${"script"}> + <script src="contentscript-iframe-subframe.js"><\/script> </head> </html>`, - "contentscript-iframe-subframe.js": `(${subframeScript})()`, + "contentscript-iframe-subframe.js": subframeScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); let win = window.open("http://example.com");
--- a/toolkit/components/extensions/test/mochitest/test_ext_tab_teardown.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_tab_teardown.html @@ -59,17 +59,17 @@ function* runTabReloadAndCloseTest(exten "ExtensionContext URL at closing tab should be tab URL"); chromeScript.sendAsyncMessage("cleanup"); chromeScript.destroy(); yield extension.unload(); } add_task(function* test_extension_page_tabs_create_reload_and_close() { - function backgroundScript() { + function background() { let tabId; browser.test.onMessage.addListener(msg => { if (msg === "open extension page") { chrome.tabs.create({url: "page.html"}, tab => { tabId = tab.id; }); } else if (msg === "reload extension page") { chrome.tabs.reload(tabId); @@ -81,35 +81,35 @@ add_task(function* test_extension_page_t }); } function pageScript() { browser.test.sendMessage("extension page loaded", document.URL); } let extensionData = { - background: `(${backgroundScript})();`, + background, files: { "page.html": `<!DOCTYPE html><meta charset="utf-8"><script src="page.js"><\/script>`, - "page.js": `(${pageScript})();`, + "page.js": pageScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); yield* runTabReloadAndCloseTest(extension); }); add_task(function* test_extension_page_window_open_reload_and_close() { // This tests whether a context that is opened via window.open is properly // disposed when the tab closes. // The background page cannot use window.open (bugzil.la/1282021), so we open // another extension page that manages the window.open-tab for testing. - function backgroundScript() { + function background() { chrome.tabs.create({url: "window.open.html"}); } function windowOpenScript() { let win; browser.test.onMessage.addListener(msg => { if (msg === "open extension page") { win = window.open("page.html"); @@ -126,22 +126,22 @@ add_task(function* test_extension_page_w browser.test.sendMessage("setup-intermediate-tab"); } function pageScript() { browser.test.sendMessage("extension page loaded", document.URL); } let extensionData = { - background: `(${backgroundScript})();`, + background, files: { "page.html": `<!DOCTYPE html><meta charset="utf-8"><script src="page.js"><\/script>`, - "page.js": `(${pageScript})();`, + "page.js": pageScript, "window.open.html": `<!DOCTYPE html><meta charset="utf-8"><script src="window.open.js"><\/script>`, - "window.open.js": `(${windowOpenScript})();`, + "window.open.js": windowOpenScript, }, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); yield extension.awaitMessage("setup-intermediate-tab"); yield* runTabReloadAndCloseTest(extension); }); </script>
--- a/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html @@ -136,43 +136,43 @@ add_task(function* test_web_accessible_r ], "web_accessible_resources": [ "/accessible.html", "wild*.html", ], }, - background: `(${background})()`, + background, files: { - "content_script.js": `(${contentScript})()`, + "content_script.js": contentScript, "accessible.html": `<html><head> <meta charset="utf-8"> - <script src="accessible.js"></${"script"}> + <script src="accessible.js"><\/script> </head></html>`, "accessible.js": 'browser.runtime.sendMessage(["page-script", location.href]);', "inaccessible.html": `<html><head> <meta charset="utf-8"> - <script src="inaccessible.js"></${"script"}> + <script src="inaccessible.js"><\/script> </head></html>`, "inaccessible.js": 'browser.runtime.sendMessage(["page-script", location.href]);', "wild1.html": `<html><head> <meta charset="utf-8"> - <script src="wild.js"></${"script"}> + <script src="wild.js"><\/script> </head></html>`, "wild2.htm": `<html><head> <meta charset="utf-8"> - <script src="wild.js"></${"script"}> + <script src="wild.js"><\/script> </head></html>`, "wild.js": 'browser.runtime.sendMessage(["page-script", location.href]);', }, }); yield extension.startup(); @@ -226,21 +226,21 @@ add_task(function* test_web_accessible_r "run_at": "document_start", "js": ["content_script_helper.js", "content_script.js"], }], "web_accessible_resources": [ "image.png", "test_script.js", ], }, - background: `(${background})()`, + background, files: { "content_script_helper.js": `${testImageLoading}`, - "content_script.js": `(${content})()`, - "test_script.js": `(${testScript})()`, + "content_script.js": content, + "test_script.js": testScript, "image.png": IMAGE_ARRAYBUFFER, }, }); // This is used to watch the blocked data bounce off CSP. function examiner() { SpecialPowers.addObserver(this, "csp-on-violate-policy", false); } @@ -321,21 +321,21 @@ add_task(function* test_web_accessible_r "run_at": "document_start", "js": ["content_script_helper.js", "content_script.js"], }], "web_accessible_resources": [ "image.png", "test_script.js", ], }, - background: `(${background})()`, + background, files: { "content_script_helper.js": `${testImageLoading}`, - "content_script.js": `(${content})()`, - "test_script.js": `(${testScript})()`, + "content_script.js": content, + "test_script.js": testScript, "image.png": IMAGE_ARRAYBUFFER, }, }); SpecialPowers.setBoolPref("security.mixed_content.block_display_content", true); yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
--- a/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html @@ -127,17 +127,17 @@ add_task(function* webnav_transitions_pr } let extensionData = { manifest: { permissions: [ "webNavigation", ], }, - background: `(${backgroundScriptTransitions})()`, + background: backgroundScriptTransitions, }; let extension = ExtensionTestUtils.loadExtension(extensionData); extension.onMessage("received", ({url, event, details}) => { received.push({url, event, details}); if (event == waitingEvent && url == waitingURL) { @@ -344,17 +344,17 @@ add_task(function* webnav_transitions_pr add_task(function* webnav_ordering() { let extensionData = { manifest: { permissions: [ "webNavigation", ], }, - background: "(" + backgroundScript.toString() + ")()", + background: backgroundScript, }; let extension = ExtensionTestUtils.loadExtension(extensionData); extension.onMessage("received", ({url, event}) => { received.push({url, event}); if (event == waitingEvent && url == waitingURL) { @@ -510,17 +510,17 @@ add_task(function* webnav_error_event() } let extensionData = { manifest: { permissions: [ "webNavigation", ], }, - background: `(${backgroundScriptErrorEvent})()`, + background: backgroundScriptErrorEvent, }; let extension = ExtensionTestUtils.loadExtension(extensionData); extension.onMessage("received", ({url, event, details}) => { received.push({url, event, details}); if (event == waitingEvent && url == waitingURL) {
--- a/toolkit/components/extensions/test/mochitest/test_ext_webnavigation_filters.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_webnavigation_filters.html @@ -9,17 +9,17 @@ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="text/javascript"> "use strict"; add_task(function* test_webnav_unresolved_uri_on_expected_URI_scheme() { - function backgroundScript() { + function background() { let lastTest; function cleanupTestListeners() { if (lastTest) { let {event, okListener, failListener} = lastTest; lastTest = null; browser.test.log(`Cleanup previous test event listeners`); browser.webNavigation[event].removeListener(okListener); @@ -65,17 +65,17 @@ add_task(function* test_webnav_unresolve } let extensionData = { manifest: { permissions: [ "webNavigation", ], }, - background: "new " + backgroundScript, + background, }; let extension = ExtensionTestUtils.loadExtension(extensionData); yield extension.startup(); yield extension.awaitMessage("ready"); @@ -252,17 +252,17 @@ add_task(function* test_webnav_unresolve info("WebNavigation event filters test scenarios completed."); yield extension.unload(); win.close(); }); add_task(function* test_webnav_empty_filter_validation_error() { - function backgroundScript() { + function background() { let catchedException; try { browser.webNavigation.onCompleted.addListener( // Empty callback (not really used) () => {}, // Empty filter (which should raise a validation error exception). {url: []} @@ -282,17 +282,17 @@ add_task(function* test_webnav_empty_fil } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: [ "webNavigation", ], }, - background: "new " + backgroundScript, + background, }); yield extension.startup(); yield extension.awaitFinish("webNav.emptyFilterValidationError"); yield extension.unload(); });
--- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm +++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm @@ -516,22 +516,16 @@ this.DownloadIntegration = { * { * shouldBlock: Whether the download should be blocked. * verdict: Detailed reason for the block, according to the * "Downloads.Error.BLOCK_VERDICT_" constants, or empty * string if the reason is unknown. * } */ shouldBlockForReputationCheck(aDownload) { -#ifndef MOZ_URL_CLASSIFIER - return Promise.resolve({ - shouldBlock: false, - verdict: "", - }); -#else let hash; let sigInfo; let channelRedirects; try { hash = aDownload.saver.getSha256Hash(); sigInfo = aDownload.saver.getSignatureInfo(); channelRedirects = aDownload.saver.getRedirects(); } catch (ex) { @@ -562,17 +556,16 @@ this.DownloadIntegration = { redirects: channelRedirects }, function onComplete(aShouldBlock, aRv, aVerdict) { deferred.resolve({ shouldBlock: aShouldBlock, verdict: (aShouldBlock && kVerdictMap[aVerdict]) || "", }); }); return deferred.promise; -#endif }, #ifdef XP_WIN /** * Checks whether downloaded files should be marked as coming from * Internet Zone. * * @return true if files should be marked
--- a/toolkit/components/moz.build +++ b/toolkit/components/moz.build @@ -55,16 +55,17 @@ DIRS += [ 'startup', 'statusfilter', 'telemetry', 'thumbnails', 'timermanager', 'tooltiptext', 'typeaheadfind', 'utils', + 'url-classifier', 'urlformatter', 'viewconfig', 'workerloader', 'xulstore' ] if CONFIG['MOZ_BUILD_APP'] != 'mobile/android': DIRS += ['narrate', 'viewsource']; @@ -82,19 +83,16 @@ if CONFIG['MOZ_FEEDS']: DIRS += ['feeds'] if CONFIG['MOZ_XUL']: DIRS += ['autocomplete', 'satchel'] if CONFIG['MOZ_TOOLKIT_SEARCH']: DIRS += ['search'] -if CONFIG['MOZ_URL_CLASSIFIER']: - DIRS += ['url-classifier'] - DIRS += ['captivedetect'] if CONFIG['OS_TARGET'] != 'Android': DIRS += ['terminator'] DIRS += ['build'] if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -913,25 +913,25 @@ var LoginManagerContent = { let mockPassword = { name: newPasswordField.name, value: newPasswordField.value }; let mockOldPassword = oldPasswordField ? { name: oldPasswordField.name, value: oldPasswordField.value } : null; // Make sure to pass the opener's top in case it was in a frame. - let opener = win.opener ? win.opener.top : null; + let openerTopWindow = win.opener ? win.opener.top : null; messageManager.sendAsyncMessage("RemoteLogins:onFormSubmit", { hostname: hostname, formSubmitURL: formSubmitURL, usernameField: mockUsername, newPasswordField: mockPassword, oldPasswordField: mockOldPassword }, - { openerWin: opener }); + { openerTopWindow }); }, /** * Attempt to find the username and password fields in a form, and fill them * in using the provided logins and recipes. * * @param {HTMLFormElement} form * @param {bool} autofillForm denotes if we should fill the form in automatically
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm @@ -79,17 +79,17 @@ var LoginManagerParent = { case "RemoteLogins:onFormSubmit": { // TODO Verify msg.target's principals against the formOrigin? this.onFormSubmit(data.hostname, data.formSubmitURL, data.usernameField, data.newPasswordField, data.oldPasswordField, - msg.objects.openerWin, + msg.objects.openerTopWindow, msg.target); break; } case "RemoteLogins:updateLoginFormPresence": { this.updateLoginFormPresence(msg.target, data); break; } @@ -282,31 +282,24 @@ var LoginManagerParent = { target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted", { requestId: requestId, logins: jsLogins, }); }, onFormSubmit: function(hostname, formSubmitURL, usernameField, newPasswordField, - oldPasswordField, opener, + oldPasswordField, openerTopWindow, target) { function getPrompter() { var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"]. createInstance(Ci.nsILoginManagerPrompter); - // XXX For E10S, we don't want to use the browser's contentWindow - // because it's in another process, so we use our chrome window as - // the window parent (the content process is responsible for - // making sure that its window is not in private browsing mode). - // In the same-process case, we can simply use the content window. - prompterSvc.init(target.isRemoteBrowser ? - target.ownerDocument.defaultView : - target.contentWindow); - if (target.isRemoteBrowser) - prompterSvc.setE10sData(target, opener); + prompterSvc.init(target.ownerDocument.defaultView); + prompterSvc.browser = target; + prompterSvc.opener = openerTopWindow; return prompterSvc; } function recordLoginUse(login) { // Update the lastUsed timestamp and increment the use count. let propBag = Cc["@mozilla.org/hash-property-bag;1"]. createInstance(Ci.nsIWritablePropertyBag); propBag.setProperty("timeLastUsed", Date.now());
--- a/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl +++ b/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl @@ -14,28 +14,36 @@ interface nsILoginManagerPrompter : nsIS /** * Initialize the prompter. Must be called before using other interfaces. * * @param aWindow * The in which the user is doing some login-related action that's * resulting in a need to prompt them for something. The prompt * will be associated with this window (or, if a notification bar * is being used, topmost opener in some cases). + * + * If this window is a content window, the corresponding window and browser + * elements will be calculated. If this window is a chrome window, the + * corresponding browser element needs to be set using setBrowser. */ void init(in nsIDOMWindow aWindow); /** - * If the caller knows which browser this prompter is being created for, - * they can call this function to avoid having to calculate it from the - * window passed to init. - * - * @param aBrowser the <browser> to use for this prompter. - * @param aOpener the opener to use for this prompter. + * The browser this prompter is being created for. + * This is required if the init function received a chrome window as argument. */ - void setE10sData(in nsIDOMElement aBrowser, in nsIDOMWindow aOpener); + attribute nsIDOMElement browser; + + /** + * The opener that was used to open the window passed to init. + * The opener can be used to determine in which window the prompt + * should be shown. Must be a content window that is not a frame window, + * make sure to pass the top window using e.g. window.top. + */ + attribute nsIDOMWindow opener; /** * Ask the user if they want to save a login (Yes, Never, Not Now) * * @param aLogin * The login to be saved. */ void promptToSavePassword(in nsILoginInfo aLogin);
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js +++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js @@ -252,18 +252,18 @@ LoginManagerPrompter.prototype = { } catch (e) { } } return this.__ellipsis; }, // Whether we are in private browsing mode get _inPrivateBrowsing() { - if (this._window) { - return PrivateBrowsingUtils.isContentWindowPrivate(this._window); + if (this._chromeWindow) { + return PrivateBrowsingUtils.isWindowPrivate(this._chromeWindow); } // If we don't that we're in private browsing mode if the caller did // not provide a window. The callers which really care about this // will indeed pass down a window to us, and for those who don't, // we can just assume that we don't want to save the entered login // information. return true; }, @@ -285,17 +285,17 @@ LoginManagerPrompter.prototype = { Cr.NS_ERROR_NOT_IMPLEMENTED); this.log("===== prompt() called ====="); if (aDefaultText) { aResult.value = aDefaultText; } - return this._promptService.prompt(this._window, + return this._promptService.prompt(this._chromeWindow, aDialogTitle, aText, aResult, null, {}); }, /** * Looks up a username and password in the database. Will prompt the user * with a dialog, even if a username and password are found. */ @@ -347,17 +347,17 @@ LoginManagerPrompter.prototype = { aUsername.value = selectedLogin.username; // If the caller provided a password, prefer it. if (!aPassword.value) aPassword.value = selectedLogin.password; } } } - var ok = this._promptService.promptUsernameAndPassword(this._window, + var ok = this._promptService.promptUsernameAndPassword(this._chromeWindow, aDialogTitle, aText, aUsername, aPassword, checkBoxLabel, checkBox); if (!ok || !checkBox.value || !hostname) return ok; if (!aPassword.value) { this.log("No password entered, so won't offer to save."); @@ -438,17 +438,17 @@ LoginManagerPrompter.prototype = { aPassword.value = foundLogins[i].password; // wallet returned straight away, so this mimics that code return true; } } } } - var ok = this._promptService.promptPassword(this._window, aDialogTitle, + var ok = this._promptService.promptPassword(this._chromeWindow, aDialogTitle, aText, aPassword, checkBoxLabel, checkBox); if (ok && checkBox.value && hostname && aPassword.value) { var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"]. createInstance(Ci.nsILoginInfo); newLogin.init(hostname, null, realm, username, aPassword.value, "", ""); @@ -558,19 +558,19 @@ LoginManagerPrompter.prototype = { // Ignore any errors and display the prompt anyway. epicfail = true; Components.utils.reportError("LoginManagerPrompter: " + "Epic fail in promptAuth: " + e + "\n"); } var ok = canAutologin; if (!ok) { - if (this._window) - PromptUtils.fireDialogEvent(this._window, "DOMWillOpenModalDialog", this._browser); - ok = this._promptService.promptAuth(this._window, + if (this._chromeWindow) + PromptUtils.fireDialogEvent(this._chromeWindow, "DOMWillOpenModalDialog", this._browser); + ok = this._promptService.promptAuth(this._chromeWindow, aChannel, aLevel, aAuthInfo, checkboxLabel, checkbox); } // If there's a notification box, use it to allow the user to // determine if the login should be saved. If there isn't a // notification box, only save the login if the user set the // checkbox to do so. @@ -674,30 +674,37 @@ LoginManagerPrompter.prototype = { }, /* ---------- nsILoginManagerPrompter prompts ---------- */ - init : function (aWindow, aFactory) { - this._window = aWindow; + if (aWindow instanceof Ci.nsIDOMChromeWindow) { + this._chromeWindow = aWindow; + // needs to be set explicitly using setBrowser + this._browser = null; + } else { + let {win, browser} = this._getChromeWindow(aWindow); + this._chromeWindow = win; + this._browser = browser; + } + this._opener = null; this._factory = aFactory || null; - this._browser = null; - this._opener = null; this.log("===== initialized ====="); }, - setE10sData : function (aBrowser, aOpener) { - if (!(this._window instanceof Ci.nsIDOMChromeWindow)) - throw new Error("Unexpected call"); + set browser(aBrowser) { this._browser = aBrowser; + }, + + set opener(aOpener) { this._opener = aOpener; }, promptToSavePassword : function (aLogin) { this.log("promptToSavePassword"); var notifyObj = this._getPopupNote() || this._getNotifyBox(); if (notifyObj) this._showSaveLoginNotification(notifyObj, aLogin); @@ -1116,17 +1123,17 @@ LoginManagerPrompter.prototype = { var neverButtonText = this._getLocalizedString( "neverForSiteButtonText"); var rememberButtonText = this._getLocalizedString( "rememberButtonText"); var notNowButtonText = this._getLocalizedString( "notNowButtonText"); this.log("Prompting user to save/ignore login"); - var userChoice = this._promptService.confirmEx(this._window, + var userChoice = this._promptService.confirmEx(this._chromeWindow, dialogTitle, dialogText, buttonFlags, rememberButtonText, notNowButtonText, neverButtonText, null, {}); // Returns: // 0 - Save the login // 1 - Ignore the login this time // 2 - Never save logins for this site @@ -1248,17 +1255,17 @@ LoginManagerPrompter.prototype = { else dialogText = this._getLocalizedString( "updatePasswordMsgNoUser"); var dialogTitle = this._getLocalizedString( "passwordChangeTitle"); // returns 0 for yes, 1 for no. - var ok = !this._promptService.confirmEx(this._window, + var ok = !this._promptService.confirmEx(this._chromeWindow, dialogTitle, dialogText, buttonFlags, null, null, null, null, {}); if (ok) { this.log("Updating password for user " + aOldLogin.username); this._updateLogin(aOldLogin, aNewLogin); } }, @@ -1281,17 +1288,17 @@ LoginManagerPrompter.prototype = { var usernames = logins.map(l => l.username); var dialogText = this._getLocalizedString("userSelectText"); var dialogTitle = this._getLocalizedString("passwordChangeTitle"); var selectedIndex = { value: null }; // If user selects ok, outparam.value is set to the index // of the selected username. - var ok = this._promptService.select(this._window, + var ok = this._promptService.select(this._chromeWindow, dialogTitle, dialogText, usernames.length, usernames, selectedIndex); if (ok) { // Now that we know which login to use, modify its password. var selectedLogin = logins[selectedIndex.value]; this.log("Updating password for user " + selectedLogin.username); var newLoginWithUsername = Cc["@mozilla.org/login-manager/loginInfo;1"]. @@ -1326,135 +1333,83 @@ LoginManagerPrompter.prototype = { // value as timeLastUsed. propBag.setProperty("timePasswordChanged", now); } propBag.setProperty("timeLastUsed", now); propBag.setProperty("timesUsedIncrement", 1); this._pwmgr.modifyLogin(login, propBag); }, - /** - * Given a content DOM window, returns the chrome window it's in. + * Given a content DOM window, returns the chrome window and browser it's in. */ _getChromeWindow: function (aWindow) { - // In e10s, aWindow may already be a chrome window. - if (aWindow instanceof Ci.nsIDOMChromeWindow) - return aWindow; - var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler.ownerDocument.defaultView; - return chromeWin; + let windows = Services.wm.getEnumerator(null); + while (windows.hasMoreElements()) { + let win = windows.getNext(); + let browser = win.gBrowser.getBrowserForContentWindow(aWindow); + if (browser) { + return { win, browser }; + } + } + return null; }, - _getNotifyWindow: function () { - - try { - // Get topmost window, in case we're in a frame. - var notifyWin = this._window.top; - var isE10s = (notifyWin instanceof Ci.nsIDOMChromeWindow); - var useOpener = false; - - // Some sites pop up a temporary login window, which disappears - // upon submission of credentials. We want to put the notification - // bar in the opener window if this seems to be happening. - if (notifyWin.opener) { - var chromeDoc = this._getChromeWindow(notifyWin). - document.documentElement; - - var hasHistory; - if (isE10s) { - if (!this._browser) - throw new Error("Expected a browser in e10s"); - hasHistory = this._browser.canGoBack; - } else { - var webnav = notifyWin. - QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIWebNavigation); - hasHistory = webnav.sessionHistory.count > 1; - } + // Some sites pop up a temporary login window, which disappears + // upon submission of credentials. We want to put the notification + // bar in the opener window if this seems to be happening. + if (this._opener) { + let chromeDoc = this._chromeWindow.document.documentElement; - // Check to see if the current window was opened with chrome - // disabled, and if so use the opener window. But if the window - // has been used to visit other pages (ie, has a history), - // assume it'll stick around and *don't* use the opener. - if (chromeDoc.getAttribute("chromehidden") && !hasHistory) { - this.log("Using opener window for notification bar."); - notifyWin = notifyWin.opener; - useOpener = true; - } + // Check to see if the current window was opened with chrome + // disabled, and if so use the opener window. But if the window + // has been used to visit other pages (ie, has a history), + // assume it'll stick around and *don't* use the opener. + if (chromeDoc.getAttribute("chromehidden") && !this._browser.canGoBack) { + this.log("Using opener window for notification bar."); + return this._getChromeWindow(this._opener); } - - let browser; - if (useOpener && this._opener && isE10s) { - // In e10s, we have to reconstruct the opener browser from - // the CPOW passed in the message (and then passed to us in - // setE10sData). - // NB: notifyWin is now the chrome window for the opening - // window. + } - browser = notifyWin.gBrowser.getBrowserForContentWindow(this._opener); - } else if (isE10s) { - browser = this._browser; - } else { - var chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; - browser = chromeWin.gBrowser - .getBrowserForDocument(notifyWin.top.document); - } - - return { notifyWin: notifyWin, browser: browser }; - - } catch (e) { - // If any errors happen, just assume no notification box. - this.log("Unable to get notify window: " + e.fileName + ":" + e.lineNumber + ": " + e.message); - return null; - } + return { win: this._chromeWindow, browser: this._browser }; }, - /** * Returns the popup notification to this prompter, * or null if there isn't one available. */ _getPopupNote : function () { let popupNote = null; try { - let { notifyWin } = this._getNotifyWindow(); + let { win: notifyWin } = this._getNotifyWindow(); - // Get the chrome window for the content window we're using. // .wrappedJSObject needed here -- see bug 422974 comment 5. - let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; - - popupNote = chromeWin.PopupNotifications; + popupNote = notifyWin.wrappedJSObject.PopupNotifications; } catch (e) { this.log("Popup notifications not available on window"); } return popupNote; }, /** * Returns the notification box to this prompter, or null if there isn't * a notification box available. */ _getNotifyBox : function () { let notifyBox = null; try { - let { notifyWin } = this._getNotifyWindow(); + let { win: notifyWin } = this._getNotifyWindow(); - // Get the chrome window for the content window we're using. // .wrappedJSObject needed here -- see bug 422974 comment 5. - let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; - - notifyBox = chromeWin.getNotificationBox(notifyWin); + notifyBox = notifyWin.wrappedJSObject.getNotificationBox(notifyWin); } catch (e) { this.log("Notification bars not available on window"); } return notifyBox; },
--- a/toolkit/components/passwordmgr/test/browser/browser.ini +++ b/toolkit/components/passwordmgr/test/browser/browser.ini @@ -25,17 +25,16 @@ support-files = subtst_notifications_4.html subtst_notifications_5.html subtst_notifications_6.html subtst_notifications_8.html subtst_notifications_9.html subtst_notifications_10.html subtst_notifications_change_p.html [browser_capture_doorhanger_window_open.js] -skip-if = e10s # Bug 1266836 - Prompt code is broken with popups in e10s support-files = subtst_notifications_11.html subtst_notifications_11_popup.html [browser_username_select_dialog.js] support-files = subtst_notifications_change_p.html [browser_DOMFormHasPassword.js] [browser_DOMInputPasswordAdded.js]
--- a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js @@ -50,17 +50,17 @@ add_task(function* test_install_addon_th yield new Promise(resolve => setTimeout(resolve, 100)); yield addon.run(topic, 10, realListener); // Waiting a little – listeners are buffered. yield new Promise(resolve => setTimeout(resolve, 100)); Assert.ok(realListener.triggered, `1. The real listener was triggered ${topic}`); Assert.ok(universalListener.triggered, `1. The universal listener was triggered ${topic}`); Assert.ok(!fakeListener.triggered, `1. The fake listener was not triggered ${topic}`); - Assert.ok(realListener.result >= 200000, `1. jank is at least 300ms (${realListener.result/1000}ms) ${topic}`); + Assert.ok(realListener.result >= addon.jankThreshold, `1. jank is at least ${addon.jankThreshold/1000}ms (${realListener.result/1000}ms) ${topic}`); info(`Attempting to remove a performance listener incorrectly, check that this does not hurt our real listener ${topic}`); Assert.throws(() => PerformanceWatcher.removePerformanceListener({addonId: addon.addonId}, () => {})); Assert.throws(() => PerformanceWatcher.removePerformanceListener({addonId: addon.addonId + "-unbound-id-" + Math.random()}, realListener.listener)); yield addon.run(topic, 10, realListener); // Waiting a little – listeners are buffered. yield new Promise(resolve => setTimeout(resolve, 300));
--- a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js @@ -13,13 +13,13 @@ add_task(function* test_watch_addon_then } throw new Error(`I shouldn't have been called with addon ${group.addonId}`); }); info("Now install the add-on, *after* having installed the listener"); let addon = new AddonBurner(addonId); Assert.ok((yield addon.run(topic, 10, realListener)), `5. The real listener was triggered ${topic}`); - Assert.ok(realListener.result >= 200000, `5. jank is at least 200ms (${realListener.result}µs) ${topic}`); + Assert.ok(realListener.result >= addon.jankThreshold, `5. jank is at least ${addon.jankThreshold/1000}ms (${realListener.result}µs) ${topic}`); realListener.unregister(); addon.dispose(); } });
--- a/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js @@ -3,17 +3,17 @@ /** * Tests for PerformanceWatcher watching slow web pages. */ /** * Simulate a slow webpage. */ function WebpageBurner() { - CPUBurner.call(this, "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random()); + CPUBurner.call(this, "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random(), 300000); } WebpageBurner.prototype = Object.create(CPUBurner.prototype); WebpageBurner.prototype.promiseBurnContentCPU = function() { return promiseContentResponse(this._browser, "test-performance-watcher:burn-content-cpu", {}); }; function WebpageListener(windowId, accept) { info(`Creating WebpageListener for ${windowId}`);
--- a/toolkit/components/perfmonitoring/tests/browser/head.js +++ b/toolkit/components/perfmonitoring/tests/browser/head.js @@ -3,46 +3,48 @@ var { utils: Cu, interfaces: Ci, classes: Cc } = Components; Cu.import("resource://gre/modules/Promise.jsm", this); Cu.import("resource://gre/modules/AddonManager.jsm", this); Cu.import("resource://gre/modules/AddonWatcher.jsm", this); Cu.import("resource://gre/modules/PerformanceWatcher.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); +Cu.import("resource://testing-common/ContentTaskUtils.jsm", this); /** * Base class for simulating slow addons/webpages. */ -function CPUBurner(url) { +function CPUBurner(url, jankThreshold) { info(`CPUBurner: Opening tab for ${url}\n`); this.url = url; this.tab = gBrowser.addTab(url); + this.jankThreshold = jankThreshold; let browser = this.tab.linkedBrowser; this._browser = browser; ContentTask.spawn(this._browser, null, CPUBurner.frameScript); this.promiseInitialized = BrowserTestUtils.browserLoaded(browser); } CPUBurner.prototype = { get windowId() { return this._browser.outerWindowID; }, /** - * Burn CPU until it triggers a listener. + * Burn CPU until it triggers a listener with the specified jank threshold. */ run: Task.async(function*(burner, max, listener) { listener.reset(); for (let i = 0; i < max; ++i) { yield new Promise(resolve => setTimeout(resolve, 50)); try { yield this[burner](); } catch (ex) { return false; } - if (listener.triggered) { + if (listener.triggered && listener.result >= this.jankThreshold) { return true; } } return false; }), dispose: function() { info(`CPUBurner: Closing tab for ${this.url}\n`); gBrowser.removeTab(this.tab); @@ -156,17 +158,18 @@ AlertListener.prototype = { this.result = null; }, }; /** * Simulate a slow add-on. */ function AddonBurner(addonId = "fake add-on id: " + Math.random()) { - CPUBurner.call(this, `http://example.com/?uri=${addonId}`) + this.jankThreshold = 200000; + CPUBurner.call(this, `http://example.com/?uri=${addonId}`, this.jankThreshold); this._addonId = addonId; this._sandbox = Components.utils.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId: this._addonId }); this._CPOWBurner = null; this._promiseCPOWBurner = new Promise(resolve => { this._browser.messageManager.addMessageListener("test-performance-watcher:cpow-init", msg => { // Note that we cannot resolve Promises with CPOWs now that they // have been outlawed in bug 1233497, so we stash it in the
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -8204,52 +8204,16 @@ "n_buckets": 20, "description": "Tracking how ServiceWorkerRegistrar loads data before the first content is shown. File bugs in Core::DOM in case of a Telemetry regression." }, "SERVICE_WORKER_REQUEST_PASSTHROUGH": { "expires_in_version": "50", "kind": "boolean", "description": "Intercepted fetch sending back same Request object. File bugs in Core::DOM in case of a Telemetry regression." }, - "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS": { - "expires_in_version": "never", - "kind": "boolean", - "description": "Stores 1 if generating a call URL succeeded, and 0 if it failed." - }, - "LOOP_CLIENT_CALL_URL_SHARED": { - "expires_in_version": "never", - "kind": "boolean", - "description": "Stores 1 every time the URL is copied or shared." - }, - "LOOP_ROOM_CREATE": { - "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"], - "expires_in_version": "54", - "kind": "enumerated", - "n_values": 4, - "releaseChannelCollection": "opt-out", - "description": "Number of times a room create action is performed (0=CREATE_SUCCESS, 1=CREATE_FAIL)" - }, - "LOOP_COPY_PANEL_ACTIONS": { - "alert_emails": ["firefox-dev@mozilla.org", "edilee@mozilla.com"], - "expires_in_version": "54", - "kind": "enumerated", - "n_values": 5, - "releaseChannelCollection": "opt-out", - "bug_numbers": [1239965, 1259506], - "description": "Number of times each of the following copy panel actions are triggered: 0=SHOWN, 1=NO_AGAIN, 2=NO_NEVER, 3=YES_AGAIN, 4=YES_NEVER" - }, - "LOOP_ACTIVITY_COUNTER": { - "alert_emails": ["firefox-dev@mozilla.org", "mbanner@mozilla.com"], - "expires_in_version": "54", - "kind": "enumerated", - "n_values": 5, - "releaseChannelCollection": "opt-out", - "bug_numbers": [1208416], - "description": "Number of times an action is performed by an user (0=OPEN_PANEL, 1=OPEN_CONVERSATION, 2=ROOM_OPEN, 3=ROOM_SHARE, 4=ROOM_DELETE)" - }, "E10S_STATUS": { "alert_emails": ["firefox-dev@mozilla.org"], "expires_in_version": "never", "kind": "enumerated", "n_values": 12, "releaseChannelCollection": "opt-out", "bug_numbers": [1241294], "description": "Why e10s is enabled or disabled (0=ENABLED_BY_USER, 1=ENABLED_BY_DEFAULT, 2=DISABLED_BY_USER, 3=DISABLED_IN_SAFE_MODE, 4=DISABLED_FOR_ACCESSIBILITY, 5=DISABLED_FOR_MAC_GFX, 6=DISABLED_FOR_BIDI, 7=DISABLED_FOR_ADDONS, 8=FORCE_DISABLED, 9=DISABLED_FOR_XPLAYERS, 10=DISABLED_FOR_OS_VERSION)"
--- a/toolkit/components/telemetry/histogram-whitelists.json +++ b/toolkit/components/telemetry/histogram-whitelists.json @@ -402,18 +402,16 @@ "LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS", "LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS", "LONG_REFLOW_INTERRUPTIBLE", "LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS", "LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS", "LOOP_AUDIO_QUALITY_OUTBOUND_RTT", "LOOP_CALL_DURATION", "LOOP_CALL_TYPE", - "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", - "LOOP_CLIENT_CALL_URL_SHARED", "LOOP_DATACHANNEL_NEGOTIATED", "LOOP_GET_USER_MEDIA_TYPE", "LOOP_ICE_ADD_CANDIDATE_ERRORS_GIVEN_FAILURE", "LOOP_ICE_ADD_CANDIDATE_ERRORS_GIVEN_SUCCESS", "LOOP_ICE_FAILURE_TIME", "LOOP_ICE_FINAL_CONNECTION_STATE", "LOOP_ICE_LATE_TRICKLE_ARRIVAL_TIME", "LOOP_ICE_ON_TIME_TRICKLE_ARRIVAL_TIME", @@ -1297,34 +1295,31 @@ "LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS", "LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS", "LONG_REFLOW_INTERRUPTIBLE", "LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS", "LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS", "LOOP_AUDIO_QUALITY_OUTBOUND_RTT", "LOOP_CALL_DURATION", "LOOP_CALL_TYPE", - "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", - "LOOP_CLIENT_CALL_URL_SHARED", "LOOP_DATACHANNEL_NEGOTIATED", "LOOP_GET_USER_MEDIA_TYPE", "LOOP_ICE_ADD_CANDIDATE_ERRORS_GIVEN_FAILURE", "LOOP_ICE_ADD_CANDIDATE_ERRORS_GIVEN_SUCCESS", "LOOP_ICE_FAILURE_TIME", "LOOP_ICE_FINAL_CONNECTION_STATE", "LOOP_ICE_LATE_TRICKLE_ARRIVAL_TIME", "LOOP_ICE_ON_TIME_TRICKLE_ARRIVAL_TIME", "LOOP_ICE_SUCCESS_RATE", "LOOP_ICE_SUCCESS_TIME", "LOOP_MAX_AUDIO_RECEIVE_TRACK", "LOOP_MAX_AUDIO_SEND_TRACK", "LOOP_MAX_VIDEO_RECEIVE_TRACK", "LOOP_MAX_VIDEO_SEND_TRACK", "LOOP_RENEGOTIATIONS", - "LOOP_ROOM_CREATE", "LOOP_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS", "LOOP_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS", "LOOP_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM", "LOOP_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL", "LOOP_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL", "LOOP_VIDEO_DECODE_ERROR_TIME_PERMILLE", "LOOP_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS", "LOOP_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS",
--- a/toolkit/modules/AppConstants.jsm +++ b/toolkit/modules/AppConstants.jsm @@ -69,23 +69,16 @@ this.AppConstants = Object.freeze({ MOZ_DATA_REPORTING: #ifdef MOZ_DATA_REPORTING true, #else false, #endif - MOZ_SAFE_BROWSING: -#ifdef MOZ_SAFE_BROWSING - true, -#else - false, -#endif - MOZ_SANDBOX: #ifdef MOZ_SANDBOX true, #else false, #endif MOZ_CONTENT_SANDBOX:
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -6487,18 +6487,18 @@ AddonInstall.prototype = { let win = this.window; if (!win && this.browser) win = this.browser.ownerDocument.defaultView; let factory = Cc["@mozilla.org/prompter;1"]. getService(Ci.nsIPromptFactory); let prompt = factory.getPrompt(win, Ci.nsIAuthPrompt2); - if (this.browser && this.browser.isRemoteBrowser && prompt instanceof Ci.nsILoginManagerPrompter) - prompt.setE10sData(this.browser, null); + if (this.browser && prompt instanceof Ci.nsILoginManagerPrompter) + prompt.browser = this.browser; return prompt; } else if (iid.equals(Ci.nsIChannelEventSink)) { return this; } return this.badCertHandler.getInterface(iid);
--- a/widget/gtk/nsClipboard.cpp +++ b/widget/gtk/nsClipboard.cpp @@ -1025,17 +1025,17 @@ wait_for_text(GtkClipboard *clipboard) return static_cast<gchar*>(context->Wait()); } static GdkFilterReturn selection_request_filter(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) { XEvent *xevent = static_cast<XEvent*>(gdk_xevent); if (xevent->xany.type == SelectionRequest) { - if (xevent->xselectionrequest.requestor == None) + if (xevent->xselectionrequest.requestor == X11None) return GDK_FILTER_REMOVE; GdkDisplay *display = gdk_x11_lookup_xdisplay( xevent->xselectionrequest.display); if (!display) return GDK_FILTER_REMOVE; GdkWindow *window = gdk_x11_window_foreign_new_for_display(display,
--- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -447,17 +447,17 @@ nsWindow::nsWindow() mSizeState = nsSizeMode_Normal; mLastSizeMode = nsSizeMode_Normal; mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize); #ifdef MOZ_X11 mOldFocusWindow = 0; mXDisplay = nullptr; - mXWindow = None; + mXWindow = X11None; mXVisual = nullptr; mXDepth = 0; #endif /* MOZ_X11 */ mPluginType = PluginType_NONE; if (!gGlobalsInitialized) { gGlobalsInitialized = true; @@ -4582,17 +4582,17 @@ nsWindow::ClearTransparencyBitmap() #ifdef MOZ_X11 if (!mGdkWindow) return; Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow); Window xWindow = gdk_x11_window_get_xid(mGdkWindow); - XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, None, ShapeSet); + XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, X11None, ShapeSet); #endif } nsresult nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride) { if (!mShell) {
--- a/widget/tests/test_keycodes.xul +++ b/widget/tests/test_keycodes.xul @@ -3239,26 +3239,26 @@ function* runXULKeyTests() modifiers:{metaKey:1}, chars:"'", unmodifiedChars:"'"}, "reservedUnshiftedKey"); yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote, modifiers:{metaKey:1, shiftKey:1}, chars:"\"", unmodifiedChars:"'"}, "reservedShiftedKey"); } else if (IS_WIN) { yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1, - modifiers:{ctrlKey:1}, chars:";"}, + modifiers:{ctrlKey:1}, chars:""}, "unshiftedKey"); yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1, - modifiers:{ctrlKey:1, shiftKey:1}, chars:";"}, + modifiers:{ctrlKey:1, shiftKey:1}, chars:""}, "shiftedKey"); yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7, - modifiers:{ctrlKey:1}, chars:"'"}, + modifiers:{ctrlKey:1}, chars:""}, "reservedUnshiftedKey"); yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7, - modifiers:{ctrlKey:1, shiftKey:1}, chars:"'"}, + modifiers:{ctrlKey:1, shiftKey:1}, chars:""}, "reservedShiftedKey"); } // 429160 if (IS_MAC) { yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_F, modifiers:{metaKey:1, altKey:1}, chars:"\u0192", unmodifiedChars:"f"}, "commandOptionF"); @@ -3434,17 +3434,17 @@ function* runReservedKeyTests() if (IS_MAC) { // Cmd+T is reserved for opening new tab. yield testReservedKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_T, modifiers:{metaKey:1}, chars:"t", unmodifiedChars:"t"}); } else if (IS_WIN) { // Ctrl+T is reserved for opening new tab. yield testReservedKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T, - modifiers:{ctrlKey:1}, chars:"t"}); + modifiers:{ctrlKey:1}, chars:"\u0014"}); } finializeKeyElementTest(); } function* runTextInputTests() { var textbox = document.getElementById("textbox");
--- a/widget/windows/KeyboardLayout.cpp +++ b/widget/windows/KeyboardLayout.cpp @@ -690,16 +690,17 @@ NativeKey::NativeKey(nsWindowBase* aWidg , mModKeyState(aModKeyState) , mVirtualKeyCode(0) , mOriginalVirtualKeyCode(0) , mShiftedLatinChar(0) , mUnshiftedLatinChar(0) , mScanCode(0) , mIsExtended(false) , mIsDeadKey(false) + , mIsFollowedByNonControlCharMessage(false) , mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ? aFakeCharMsgs : nullptr) { MOZ_ASSERT(aWidget); MOZ_ASSERT(mDispatcher); KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance(); mKeyboardLayout = keyboardLayout->GetLayout(); if (aOverrideKeyboardLayout && mKeyboardLayout != aOverrideKeyboardLayout) { @@ -874,28 +875,35 @@ NativeKey::NativeKey(nsWindowBase* aWidg } if (!mVirtualKeyCode) { mVirtualKeyCode = mOriginalVirtualKeyCode; } mDOMKeyCode = keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode); - mKeyNameIndex = + // Be aware, keyboard utilities can change non-printable keys to printable + // keys. In such case, we should make the key value as a printable key. + mIsFollowedByNonControlCharMessage = + IsKeyDownMessage() && IsFollowedByNonControlCharMessage(); + mKeyNameIndex = mIsFollowedByNonControlCharMessage ? + KEY_NAME_INDEX_USE_STRING : keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode); mCodeNameIndex = KeyboardLayout::ConvertScanCodeToCodeNameIndex( GetScanCodeWithExtendedFlag()); keyboardLayout->InitNativeKey(*this, mModKeyState); mIsDeadKey = (IsFollowedByDeadCharMessage() || keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState)); - mIsPrintableKey = KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode); + mIsPrintableKey = + mKeyNameIndex == KEY_NAME_INDEX_USE_STRING || + KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode); if (IsKeyDownMessage()) { // Compute some strings which may be inputted by the key with various // modifier state if this key event won't cause text input actually. // They will be used for setting mAlternativeCharCodes in the callback // method which will be called by TextEventDispatcher. if (NeedsToHandleWithoutFollowingCharMessages()) { ComputeInputtingStringWithKeyboardLayout(); @@ -1050,16 +1058,34 @@ NativeKey::IsFollowedByDeadCharMessage() PM_NOREMOVE | PM_NOYIELD)) { return false; } } return IsDeadCharMessage(nextMsg); } bool +NativeKey::IsFollowedByNonControlCharMessage() const +{ + MSG nextMsg; + if (mFakeCharMsgs) { + nextMsg = mFakeCharMsgs->ElementAt(0).GetCharMsg(mMsg.hwnd); + } else if (IsKeyMessageOnPlugin()) { + return false; + } else { + if (!WinUtils::PeekMessage(&nextMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST, + PM_NOREMOVE | PM_NOYIELD)) { + return false; + } + } + return nextMsg.message == WM_CHAR && + !IsControlChar(static_cast<char16_t>(nextMsg.wParam)); +} + +bool NativeKey::IsIMEDoingKakuteiUndo() const { // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG: // --------------------------------------------------------------------------- // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1) // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0) // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF) @@ -1838,16 +1864,23 @@ NativeKey::NeedsToHandleWithoutFollowing } // If inputting two or more characters, should be dispatched after removing // whole following char messages. if (mCommittedCharsAndModifiers.mLength > 1) { return true; } + // If keydown message is followed by WM_CHAR whose wParam isn't a control + // character, we should dispatch keypress event with the char message + // even with any modifier state. + if (mIsFollowedByNonControlCharMessage) { + return false; + } + // If any modifier keys which may cause printable keys becoming non-printable // are not pressed, we don't need special handling for the key. if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() && !mModKeyState.IsWin()) { return false; } // If the key event causes dead key event, we don't need to dispatch keypress @@ -2517,16 +2550,43 @@ KeyboardLayout::IsDeadKey(uint8_t aVirtu if (virtualKeyIndex < 0) { return false; } return mVirtualKeys[virtualKeyIndex].IsDeadKey( VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers())); } +bool +KeyboardLayout::IsSysKey(uint8_t aVirtualKey, + const ModifierKeyState& aModKeyState) const +{ + // If Alt key is not pressed, it's never a system key combination. + // Additionally, if Ctrl key is pressed, it's never a system key combination + // too. + if (!aModKeyState.IsAlt() || aModKeyState.IsControl()) { + return false; + } + + int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey); + if (virtualKeyIndex < 0) { + return true; + } + + UniCharsAndModifiers inputCharsAndModifiers = + GetUniCharsAndModifiers(aVirtualKey, aModKeyState); + if (inputCharsAndModifiers.IsEmpty()) { + return true; + } + + // If the Alt key state isn't consumed, that means that the key with Alt + // doesn't cause text input. So, the combination is a system key. + return inputCharsAndModifiers.mModifiers[0] != MODIFIER_ALT; +} + void KeyboardLayout::InitNativeKey(NativeKey& aNativeKey, const ModifierKeyState& aModKeyState) { if (mIsPendingToRestoreKeyboardLayout) { LoadLayout(::GetKeyboardLayout(0)); } @@ -3425,18 +3485,20 @@ KeyboardLayout::SynthesizeNativeKeyEvent UINT scanCode = ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key); LPARAM lParam = static_cast<LPARAM>(scanCode << 16); // Add extended key flag to the lParam for right control key and right alt // key. if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) { lParam |= 0x1000000; } - MSG keyDownMsg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam, - aWidget->GetWindowHandle()); + bool makeSysKeyMsg = IsSysKey(key, modKeyState); + MSG keyDownMsg = + WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYDOWN : WM_KEYDOWN, + key, lParam, aWidget->GetWindowHandle()); if (i == keySequence.Length() - 1) { bool makeDeadCharMsg = (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty()); nsAutoString chars(aCharacters); if (makeDeadCharMsg) { UniCharsAndModifiers deadChars = GetUniCharsAndModifiers(key, modKeyState); chars = deadChars.ToString(); @@ -3447,16 +3509,17 @@ KeyboardLayout::SynthesizeNativeKeyEvent NativeKey nativeKey(aWidget, keyDownMsg, modKeyState); nativeKey.HandleKeyDownMessage(); } else { AutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs; for (uint32_t j = 0; j < chars.Length(); j++) { NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement(); fakeCharMsg->mCharCode = chars.CharAt(j); fakeCharMsg->mScanCode = scanCode; + fakeCharMsg->mIsSysKey = makeSysKeyMsg; fakeCharMsg->mIsDeadKey = makeDeadCharMsg; } NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, 0, &fakeCharMsgs); bool dispatched; nativeKey.HandleKeyDownMessage(&dispatched); // If some char messages are not consumed, let's emulate the widget // receiving the message directly. for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) { @@ -3485,17 +3548,20 @@ KeyboardLayout::SynthesizeNativeKeyEvent UINT scanCode = ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key); LPARAM lParam = static_cast<LPARAM>(scanCode << 16); // Add extended key flag to the lParam for right control key and right alt // key. if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) { lParam |= 0x1000000; } - MSG keyUpMsg = WinUtils::InitMSG(WM_KEYUP, key, lParam, + // Don't use WM_SYSKEYUP for Alt keyup. + bool makeSysKeyMsg = IsSysKey(key, modKeyState) && key != VK_MENU; + MSG keyUpMsg = WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYUP : WM_KEYUP, + key, lParam, aWidget->GetWindowHandle()); NativeKey nativeKey(aWidget, keyUpMsg, modKeyState); nativeKey.HandleKeyUpMessage(); } // Restore old key state and layout ::SetKeyboardState(originalKbdState); RestoreLayout();
--- a/widget/windows/KeyboardLayout.h +++ b/widget/windows/KeyboardLayout.h @@ -175,29 +175,37 @@ class MOZ_STACK_CLASS NativeKey final { friend class KeyboardLayout; public: struct FakeCharMsg { UINT mCharCode; UINT mScanCode; + bool mIsSysKey; bool mIsDeadKey; bool mConsumed; - FakeCharMsg() : - mCharCode(0), mScanCode(0), mIsDeadKey(false), mConsumed(false) + FakeCharMsg() + : mCharCode(0) + , mScanCode(0) + , mIsSysKey(false) + , mIsDeadKey(false) + , mConsumed(false) { } MSG GetCharMsg(HWND aWnd) const { MSG msg; msg.hwnd = aWnd; - msg.message = mIsDeadKey ? WM_DEADCHAR : WM_CHAR; + msg.message = mIsDeadKey && mIsSysKey ? WM_SYSDEADCHAR : + mIsDeadKey ? WM_DEADCHAR : + mIsSysKey ? WM_SYSCHAR : + WM_CHAR; msg.wParam = static_cast<WPARAM>(mCharCode); msg.lParam = static_cast<LPARAM>(mScanCode << 16); msg.time = 0; msg.pt.x = msg.pt.y = 0; return msg; } }; @@ -300,16 +308,20 @@ private: // mIsPrintableKey is true if the key may be a printable key without // any modifier keys. Otherwise, false. // Please note that the event may not cause any text input even if this // is true. E.g., it might be dead key state or Ctrl key may be pressed. bool mIsPrintableKey; // mIsOverridingKeyboardLayout is true if the instance temporarily overriding // keyboard layout with specified by the constructor. bool mIsOverridingKeyboardLayout; + // mIsFollowedByNonControlCharMessage may be true when mMsg is a keydown + // message. When the keydown message is followed by a char message, this + // is true. + bool mIsFollowedByNonControlCharMessage; nsTArray<FakeCharMsg>* mFakeCharMsgs; // When a keydown event is dispatched at handling WM_APPCOMMAND, the computed // virtual keycode is set to this. Even if we consume WM_APPCOMMAND message, // Windows may send WM_KEYDOWN and WM_KEYUP message for them. // At that time, we should not dispatch key events for them. static uint8_t sDispatchedKeyOfAppCommand; @@ -407,16 +419,17 @@ private: { return IsSysCharMessage(aMSG.message); } bool IsSysCharMessage(UINT aMessage) const { return (aMessage == WM_SYSCHAR || aMessage == WM_SYSDEADCHAR); } bool MayBeSameCharMessage(const MSG& aCharMsg1, const MSG& aCharMsg2) const; + bool IsFollowedByNonControlCharMessage() const; bool IsFollowedByDeadCharMessage() const; bool IsKeyMessageOnPlugin() const { return (mMsg.message == MOZ_WM_KEYDOWN || mMsg.message == MOZ_WM_KEYUP); } /** @@ -580,16 +593,23 @@ public: /** * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState. * This method isn't stateful. */ bool IsDeadKey(uint8_t aVirtualKey, const ModifierKeyState& aModKeyState) const; /** + * IsSysKey() returns true if aVirtualKey with aModKeyState causes WM_SYSKEY* + * or WM_SYS*CHAR messages. + */ + bool IsSysKey(uint8_t aVirtualKey, + const ModifierKeyState& aModKeyState) const; + + /** * GetUniCharsAndModifiers() returns characters which is inputted by the * aVirtualKey with aModKeyState. This method isn't stateful. */ UniCharsAndModifiers GetUniCharsAndModifiers( uint8_t aVirtualKey, const ModifierKeyState& aModKeyState) const; /**