author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 08 Feb 2017 11:30:50 +0100 | |
changeset 341334 | c5b88e4e70f48955661dc7900b43a95c3c785836 |
parent 341278 | fdf4f0e8ff1f754cc3d49830e102e978ea181c25 (current diff) |
parent 341333 | 3a95aa4246653a7863914ffec032897d13359fb0 (diff) |
child 341335 | 43ac95c99af6c7edea7328427d78605583b14e94 |
push id | 86684 |
push user | cbook@mozilla.com |
push date | Wed, 08 Feb 2017 10:31:03 +0000 |
treeherder | mozilla-inbound@c5b88e4e70f4 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 54.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/browser/base/content/aboutNetError.xhtml +++ b/browser/base/content/aboutNetError.xhtml @@ -72,22 +72,16 @@ } catch (e) { // We probably tried to reload a URI that caused an exception to // occur; e.g. a nonexistent file. } buttonEl.disabled = true; } - function doOverride(buttonEl) { - var event = new CustomEvent("AboutNetErrorOverride", {bubbles:true}); - document.dispatchEvent(event); - retryThis(buttonEl); - } - function toggleDisplay(node) { const toggle = { "": "block", "none": "block", "block": "none" }; return (node.style.display = toggle[node.style.display]); } @@ -102,20 +96,19 @@ panel.style.display = "block"; document.getElementById("netErrorButtonContainer").style.display = "none"; document.getElementById("prefResetButton").addEventListener("click", function resetPreferences(e) { const event = new CustomEvent("AboutNetErrorResetPreferences", {bubbles:true}); document.dispatchEvent(event); }); } - function setupAdvancedButton(allowOverride) { + function setupAdvancedButton() { // Get the hostname and add it to the panel - var panelId = gIsCertError ? "badCertAdvancedPanel" : "weakCryptoAdvancedPanel"; - var panel = document.getElementById(panelId); + var panel = document.getElementById("badCertAdvancedPanel"); for (var span of panel.querySelectorAll("span.hostname")) { span.textContent = document.location.hostname; } if (!gIsCertError) { panel.replaceChild(document.getElementById("errorLongDesc"), document.getElementById("advancedLongDesc")); } @@ -133,22 +126,16 @@ if (panel.style.display == "block") { // send event to trigger telemetry ping var event = new CustomEvent("AboutNetErrorUIExpanded", {bubbles:true}); document.dispatchEvent(event); } }); - if (allowOverride) { - document.getElementById("overrideWeakCryptoPanel").style.display = "flex"; - var overrideLink = document.getElementById("overrideWeakCrypto"); - overrideLink.addEventListener("click", () => doOverride(overrideLink)); - } - if (!gIsCertError) { return; } if (getCSSClass() == "expertBadCert") { toggleDisplay(document.getElementById("badCertAdvancedPanel")); // Toggling the advanced panel must ensure that the debugging // information panel is hidden as well, since it's opened by the @@ -221,20 +208,16 @@ if (err == "sslv3Used") { document.getElementById("learnMoreContainer").style.display = "block"; let learnMoreLink = document.getElementById("learnMoreLink"); learnMoreLink.href = "https://support.mozilla.org/kb/how-resolve-sslv3-error-messages-firefox"; document.body.className = "certerror"; } - if (err == "weakCryptoUsed") { - document.body.className = "certerror"; - } - // remove undisplayed errors to avoid bug 39098 var errContainer = document.getElementById("errorContainer"); errContainer.remove(); var className = getCSSClass(); if (className && className != "expertBadCert") { // Associate a CSS class with the root of the page, if one was passed in, // to allow custom styling. @@ -260,27 +243,24 @@ if (err == "cspBlocked") { // Remove the "Try again" button for CSP violations, since it's // almost certainly useless. (Bug 553180) document.getElementById("netErrorButtonContainer").style.display = "none"; } window.addEventListener("AboutNetErrorOptions", function(evt) { // Pinning errors are of type nssFailure2 - if (getErrorCode() == "nssFailure2" || getErrorCode() == "weakCryptoUsed") { + if (getErrorCode() == "nssFailure2") { document.getElementById("learnMoreContainer").style.display = "block"; let learnMoreLink = document.getElementById("learnMoreLink"); // nssFailure2 also gets us other non-overrideable errors. Choose // a "learn more" link based on description: if (getDescription().includes("mozilla_pkix_error_key_pinning_failure")) { learnMoreLink.href = "https://support.mozilla.org/kb/certificate-pinning-reports"; } - if (getErrorCode() == "weakCryptoUsed") { - learnMoreLink.href = "https://support.mozilla.org/kb/how-resolve-weak-crypto-error-messages-firefox"; - } var options = JSON.parse(evt.detail); if (options && options.enabled) { var checkbox = document.getElementById("automaticallyReportInFuture"); showCertificateErrorReporting(); if (options.automatic) { // set the checkbox checkbox.checked = true; @@ -300,18 +280,18 @@ "SSL_ERROR_NO_CYPHER_OVERLAP", "SSL_ERROR_NO_CIPHERS_SUPPORTED" ].some((substring) => getDescription().includes(substring)); // If it looks like an error that is user config based if (getErrorCode() == "nssFailure2" && hasPrefStyleError && options && options.changedCertPrefs) { showPrefChangeContainer(); } } - if (getErrorCode() == "weakCryptoUsed" || getErrorCode() == "sslv3Used") { - setupAdvancedButton(getErrorCode() == "weakCryptoUsed"); + if (getErrorCode() == "sslv3Used") { + setupAdvancedButton(); } }, true, true); var event = new CustomEvent("AboutNetErrorLoad", {bubbles:true}); document.dispatchEvent(event); if (err == "inadequateSecurityError") { // Remove the "Try again" button for HTTP/2 inadequate security as it @@ -332,17 +312,17 @@ document.title = document.getElementById("captivePortalPageTitle").textContent; document.getElementById("openPortalLoginPageButton") .addEventListener("click", () => { let event = new CustomEvent("AboutNetErrorOpenCaptivePortal", {bubbles:true}); document.dispatchEvent(event); }); - setupAdvancedButton(true); + setupAdvancedButton(); addDomainErrorLinks(); // When the portal is freed, an event is generated by the frame script // that we can pick up and attempt to reload the original page. window.addEventListener("AboutNetErrorCaptivePortalFreed", () => { document.location.reload(); }); @@ -350,17 +330,17 @@ function initPageCertError() { document.body.className = "certerror"; document.title = document.getElementById("certErrorPageTitle").textContent; for (let host of document.querySelectorAll(".hostname")) { host.textContent = document.location.hostname; } - setupAdvancedButton(true); + setupAdvancedButton(); document.getElementById("learnMoreContainer").style.display = "block"; let checkbox = document.getElementById("automaticallyReportInFuture"); checkbox.addEventListener("change", function({target: {checked}}) { document.dispatchEvent(new CustomEvent("AboutNetErrorSetAutomatic", { detail: checked, bubbles: true @@ -484,18 +464,17 @@ * The certificate is only valid for garage.maemo.org */ if (thisHost.endsWith("." + okHost)) link.href = proto + okHost; // If we set a link, meaning there's something helpful for // the user here, expand the section by default if (link.href && getCSSClass() != "expertBadCert") { - var panelId = gIsCertError ? "badCertAdvancedPanel" : "weakCryptoAdvancedPanel" - toggleDisplay(document.getElementById(panelId)); + toggleDisplay(document.getElementById("badCertAdvancedPanel")); if (gIsCertError) { // Toggling the advanced panel must ensure that the debugging // information panel is hidden as well, since it's opened by the // error code link in the advanced panel. var div = document.getElementById("certificateErrorDebugInformation"); div.style.display = "none"; } } @@ -540,17 +519,16 @@ <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1> <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1> <h1 id="et_nssFailure2">&nssFailure2.title;</h1> <h1 id="et_nssBadCert">&certerror.longpagetitle1;</h1> <h1 id="et_cspBlocked">&cspBlocked.title;</h1> <h1 id="et_remoteXUL">&remoteXUL.title;</h1> <h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1> <h1 id="et_sslv3Used">&sslv3Used.title;</h1> - <h1 id="et_weakCryptoUsed">&weakCryptoUsed.title;</h1> <h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1> </div> <div id="errorDescriptionsContainer"> <div id="ed_generic">&generic.longDesc;</div> <div id="ed_captivePortal">&captivePortal.longDesc2;</div> <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div> <div id="ed_fileNotFound">&fileNotFound.longDesc;</div> <div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div> @@ -570,17 +548,16 @@ <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div> <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div> <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div> <div id="ed_nssBadCert">&certerror.introPara;</div> <div id="ed_cspBlocked">&cspBlocked.longDesc;</div> <div id="ed_remoteXUL">&remoteXUL.longDesc;</div> <div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div> <div id="ed_sslv3Used">&sslv3Used.longDesc2;</div> - <div id="ed_weakCryptoUsed">&weakCryptoUsed.longDesc2;</div> <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div> </div> </div> <!-- PAGE CONTAINER (for styling purposes only) --> <div id="errorPageContainer" class="container"> <!-- Error Title --> @@ -649,26 +626,16 @@ <div id="certificateErrorReporting"> <p class="toggle-container-with-text"> <input type="checkbox" id="automaticallyReportInFuture" /> <label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic2;</label> </p> </div> <div id="advancedPanelContainer"> - <div id="weakCryptoAdvancedPanel" class="advanced-panel"> - <div id="weakCryptoAdvancedDescription"> - <p>&weakCryptoAdvanced.longDesc;</p> - </div> - <div id="advancedLongDesc" /> - <div id="overrideWeakCryptoPanel"> - <a id="overrideWeakCrypto" href="#">&weakCryptoAdvanced.override;</a> - </div> - </div> - <div id="badCertAdvancedPanel" class="advanced-panel"> <p id="badCertTechnicalInfo"/> <button id="exceptionDialogButton">&securityOverride.exceptionButtonLabel;</button> </div> </div> </div>
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2841,33 +2841,31 @@ var BrowserOnClick = { mm.addMessageListener("Browser:CertExceptionError", this); mm.addMessageListener("Browser:OpenCaptivePortalPage", this); mm.addMessageListener("Browser:SiteBlockedError", this); mm.addMessageListener("Browser:EnableOnlineMode", this); mm.addMessageListener("Browser:SendSSLErrorReport", this); mm.addMessageListener("Browser:SetSSLErrorReportAuto", this); mm.addMessageListener("Browser:ResetSSLPreferences", this); mm.addMessageListener("Browser:SSLErrorReportTelemetry", this); - mm.addMessageListener("Browser:OverrideWeakCrypto", this); mm.addMessageListener("Browser:SSLErrorGoBack", this); Services.obs.addObserver(this, "captive-portal-login-abort", false); Services.obs.addObserver(this, "captive-portal-login-success", false); }, uninit() { let mm = window.messageManager; mm.removeMessageListener("Browser:CertExceptionError", this); mm.removeMessageListener("Browser:SiteBlockedError", this); mm.removeMessageListener("Browser:EnableOnlineMode", this); mm.removeMessageListener("Browser:SendSSLErrorReport", this); mm.removeMessageListener("Browser:SetSSLErrorReportAuto", this); mm.removeMessageListener("Browser:ResetSSLPreferences", this); mm.removeMessageListener("Browser:SSLErrorReportTelemetry", this); - mm.removeMessageListener("Browser:OverrideWeakCrypto", this); mm.removeMessageListener("Browser:SSLErrorGoBack", this); Services.obs.removeObserver(this, "captive-portal-login-abort"); Services.obs.removeObserver(this, "captive-portal-login-success"); }, observe(aSubject, aTopic, aData) { switch (aTopic) { @@ -2938,23 +2936,16 @@ var BrowserOnClick = { } Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI").add(bin); break; case "Browser:SSLErrorReportTelemetry": let reportStatus = msg.data.reportStatus; Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI") .add(reportStatus); break; - case "Browser:OverrideWeakCrypto": - let weakCryptoOverride = Cc["@mozilla.org/security/weakcryptooverride;1"] - .getService(Ci.nsIWeakCryptoOverride); - weakCryptoOverride.addWeakCryptoOverride( - msg.data.uri.host, - PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)); - break; case "Browser:SSLErrorGoBack": goBackFromErrorPage(); break; } }, onSSLErrorReport(browser, uri, securityInfo) { if (!Services.prefs.getBoolPref("security.ssl.errorReporting.enabled")) { @@ -6937,17 +6928,16 @@ var gIdentityHandler = { // Then, update the user interface with the available data. this.refreshIdentityBlock(); // Handle a location change while the Control Center is focused // by closing the popup (bug 1207542) if (shouldHidePopup) { this._identityPopup.hidePopup(); } - this.showWeakCryptoInfoBar(); // NOTE: We do NOT update the identity popup (the control center) when // we receive a new security state on the existing page (i.e. from a // subframe). If the user opened the popup and looks at the provided // information we don't want to suddenly change the panel contents. }, /** @@ -7127,65 +7117,16 @@ var gIdentityHandler = { // Set cropping and direction this._identityIconLabel.crop = icon_country_label ? "end" : "center"; this._identityIconLabel.parentNode.style.direction = icon_labels_dir; // Hide completely if the organization label is empty this._identityIconLabel.parentNode.collapsed = icon_label ? false : true; }, /** - * Show the weak crypto notification bar. - */ - showWeakCryptoInfoBar() { - if (!this._uriHasHost || !this._isBroken || !this._sslStatus.cipherName || - this._sslStatus.cipherName.indexOf("_RC4_") < 0) { - return; - } - - let notificationBox = gBrowser.getNotificationBox(); - let notification = notificationBox.getNotificationWithValue("weak-crypto"); - if (notification) { - return; - } - - let brandBundle = document.getElementById("bundle_brand"); - let brandShortName = brandBundle.getString("brandShortName"); - let message = gNavigatorBundle.getFormattedString("weakCryptoOverriding.message", - [brandShortName]); - - let host = this._uri.host; - let port = 443; - try { - if (this._uri.port > 0) { - port = this._uri.port; - } - } catch (e) {} - - let buttons = [{ - label: gNavigatorBundle.getString("revokeOverride.label"), - accessKey: gNavigatorBundle.getString("revokeOverride.accesskey"), - callback(aNotification, aButton) { - try { - let weakCryptoOverride = Cc["@mozilla.org/security/weakcryptooverride;1"] - .getService(Ci.nsIWeakCryptoOverride); - weakCryptoOverride.removeWeakCryptoOverride(host, port, - PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)); - BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE); - } catch (e) { - Cu.reportError(e); - } - } - }]; - - const priority = notificationBox.PRIORITY_WARNING_MEDIUM; - notificationBox.appendNotification(message, "weak-crypto", null, - priority, buttons); - }, - - /** * Set up the title and content messages for the identity message popup, * based on the specified mode, and the details of the SSL cert, where * applicable */ refreshIdentityPopup() { // Update "Learn More" for Mixed Content Blocking and Insecure Login Forms. let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); this._identityPopupMixedContentLearnMore
--- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -262,17 +262,16 @@ function getSerializedSecurityInfo(docSh var AboutNetAndCertErrorListener = { init(chromeGlobal) { addMessageListener("CertErrorDetails", this); addMessageListener("Browser:CaptivePortalFreed", this); chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true); chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true); chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true); - chromeGlobal.addEventListener("AboutNetErrorOverride", this, false, true); chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true); }, get isAboutNetError() { return content.document.documentURI.startsWith("about:neterror"); }, get isAboutCertError() { @@ -385,19 +384,16 @@ var AboutNetAndCertErrorListener = { this.onPageLoad(aEvent); break; case "AboutNetErrorOpenCaptivePortal": this.openCaptivePortalPage(aEvent); break; case "AboutNetErrorSetAutomatic": this.onSetAutomatic(aEvent); break; - case "AboutNetErrorOverride": - this.onOverride(aEvent); - break; case "AboutNetErrorResetPreferences": this.onResetPreferences(aEvent); break; } }, changedCertPrefs() { for (let prefName of PREF_SSL_IMPACT) { @@ -448,21 +444,16 @@ var AboutNetAndCertErrorListener = { let {host, port} = content.document.mozDocumentURIIfNotForErrorPages; sendAsyncMessage("Browser:SendSSLErrorReport", { uri: { host, port }, securityInfo: getSerializedSecurityInfo(docShell), }); } }, - - onOverride(evt) { - let {host, port} = content.document.mozDocumentURIIfNotForErrorPages; - sendAsyncMessage("Browser:OverrideWeakCrypto", { uri: {host, port} }); - } } AboutNetAndCertErrorListener.init(this); var ClickEventHandler = { init: function init() { Cc["@mozilla.org/eventlistenerservice;1"]
--- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -89,19 +89,16 @@ support-files = !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html !/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs !/toolkit/mozapps/extensions/test/xpinstall/restartless-unsigned.xpi !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi !/toolkit/mozapps/extensions/test/xpinstall/theme.xpi !/toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs - file_about_child.html - file_about_parent.html - file_register_about_page.js [browser_aboutAccounts.js] skip-if = os == "linux" # Bug 958026 support-files = content_aboutAccounts.js [browser_aboutCertError.js] [browser_aboutNetError.js] [browser_aboutSupport_newtab_security_state.js] @@ -422,16 +419,19 @@ skip-if = true # Bug 1005420 - fails int skip-if = (os == "win" && !debug) [browser_web_channel.js] [browser_windowopen_reflows.js] [browser_zbug569342.js] skip-if = e10s || debug # Bug 1094240 - has findbar-related failures [browser_registerProtocolHandler_notification.js] [browser_addCertException.js] [browser_e10s_about_page_triggeringprincipal.js] +support-files = + file_about_child.html + file_about_parent.html [browser_e10s_switchbrowser.js] [browser_e10s_about_process.js] [browser_e10s_chrome_process.js] [browser_e10s_javascript.js] [browser_blockHPKP.js] tags = psm [browser_windowactivation.js] [browser_contextmenu_childprocess.js]
--- a/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js +++ b/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js @@ -1,26 +1,24 @@ "use strict"; -Cu.import("resource://gre/modules/Services.jsm"); + +const kChildPage = getRootDirectory(gTestPath) + "file_about_child.html"; +const kParentPage = getRootDirectory(gTestPath) + "file_about_parent.html"; -registerCleanupFunction(function() { - Services.ppmm.broadcastAsyncMessage("AboutPrincipalTest:Unregister"); - BrowserTestUtils.waitForMessage(Services.ppmm, "AboutPrincipalTest:Unregistered").then( - Services.ppmm.removeDelayedProcessScript( - "chrome://mochitests/content/browser/browser/base/content/test/general/file_register_about_page.js" - ) - ); -}); +const kAboutPagesRegistered = Promise.all([ + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-about-principal-child", kChildPage, + Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD | Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-about-principal-parent", kParentPage, + Ci.nsIAboutModule.ALLOW_SCRIPT) +]); add_task(function* test_principal_click() { - Services.ppmm.loadProcessScript( - "chrome://mochitests/content/browser/browser/base/content/test/general/file_register_about_page.js", - true - ); - + yield kAboutPagesRegistered; yield BrowserTestUtils.withNewTab("about:test-about-principal-parent", function*(browser) { let loadPromise = BrowserTestUtils.browserLoaded(browser, false, "about:test-about-principal-child"); let myLink = browser.contentDocument.getElementById("aboutchildprincipal"); myLink.click(); yield loadPromise; yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() { let channel = content.document.docShell.currentDocumentChannel; @@ -39,16 +37,17 @@ add_task(function* test_principal_click( let loadingPrincipal = channel.loadInfo.loadingPrincipal; is(loadingPrincipal, null, "sanity check - load of TYPE_DOCUMENT must have a null loadingPrincipal"); }); }); }); add_task(function* test_principal_ctrl_click() { + yield kAboutPagesRegistered; yield SpecialPowers.pushPrefEnv({ "set": [["security.sandbox.content.level", 1]], }); yield BrowserTestUtils.withNewTab("about:test-about-principal-parent", function*(browser) { let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:test-about-principal-child"); // simulate ctrl+click BrowserTestUtils.synthesizeMouseAtCenter("#aboutchildprincipal", @@ -75,16 +74,17 @@ add_task(function* test_principal_ctrl_c is(loadingPrincipal, null, "sanity check - load of TYPE_DOCUMENT must have a null loadingPrincipal"); }); yield BrowserTestUtils.removeTab(tab); }); }); add_task(function* test_principal_right_click_open_link_in_new_tab() { + yield kAboutPagesRegistered; yield SpecialPowers.pushPrefEnv({ "set": [["security.sandbox.content.level", 1]], }); yield BrowserTestUtils.withNewTab("about:test-about-principal-parent", function*(browser) { let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:test-about-principal-child"); // simulate right-click open link in tab
--- a/browser/base/content/test/general/browser_misused_characters_in_strings.js +++ b/browser/base/content/test/general/browser_misused_characters_in_strings.js @@ -13,24 +13,16 @@ let gWhitelist = [{ key: "searchForSomethingWith", type: "single-quote" }, { file: "netError.dtd", key: "certerror.introPara", type: "single-quote" }, { file: "netError.dtd", - key: "weakCryptoAdvanced.longDesc", - type: "single-quote" - }, { - file: "netError.dtd", - key: "weakCryptoAdvanced.override", - type: "single-quote" - }, { - file: "netError.dtd", key: "inadequateSecurityError.longDesc", type: "single-quote" }, { file: "netError.dtd", key: "certerror.wrongSystemTime2", type: "single-quote" }, { file: "netError.dtd",
deleted file mode 100644 --- a/browser/base/content/test/general/file_register_about_page.js +++ /dev/null @@ -1,81 +0,0 @@ -const { interfaces: Ci, results: Cr, manager: Cm, utils: Cu } = Components; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -function AboutPage(chromeURL, aboutHost, classID, description, uriFlags) { - this.chromeURL = chromeURL; - this.aboutHost = aboutHost; - this.classID = Components.ID(classID); - this.description = description; - this.uriFlags = uriFlags; -} - -AboutPage.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), - getURIFlags(aURI) { // eslint-disable-line no-unused-vars - return this.uriFlags; - }, - - newChannel(aURI, aLoadInfo) { - let newURI = Services.io.newURI(this.chromeURL); - let channel = Services.io.newChannelFromURIWithLoadInfo(newURI, - aLoadInfo); - channel.originalURI = aURI; - - if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) { - channel.owner = null; - } - return channel; - }, - - createInstance(outer, iid) { - if (outer !== null) { - throw Cr.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(iid); - }, - - register() { - Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory( - this.classID, this.description, - "@mozilla.org/network/protocol/about;1?what=" + this.aboutHost, this); - }, - - unregister() { - Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory( - this.classID, this); - } -}; - -/* exported AboutPrincipalTest */ -var AboutPrincipalTest = {}; - -XPCOMUtils.defineLazyGetter(AboutPrincipalTest, "aboutChild", () => - new AboutPage("chrome://mochitests/content/browser/browser/base/content/test/general/file_about_child.html", - "test-about-principal-child", - "{df6cbd19-c95b-4011-874b-315347c0832c}", - "About Principal Child Test", - Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD | - Ci.nsIAboutModule.ALLOW_SCRIPT) - -); - -XPCOMUtils.defineLazyGetter(AboutPrincipalTest, "aboutParent", () => - new AboutPage("chrome://mochitests/content/browser/browser/base/content/test/general/file_about_parent.html", - "test-about-principal-parent", - "{15e1a03d-9f94-4352-bfb8-94216140d3ab}", - "About Principal Parent Test", - Ci.nsIAboutModule.ALLOW_SCRIPT) -); - -AboutPrincipalTest.aboutParent.register(); -AboutPrincipalTest.aboutChild.register(); - -function onUnregisterMessage() { - removeMessageListener("AboutPrincipalTest:Unregister", onUnregisterMessage); - AboutPrincipalTest.aboutParent.unregister(); - AboutPrincipalTest.aboutChild.unregister(); - sendAsyncMessage("AboutPrincipalTest:Unregistered"); -} - -addMessageListener("AboutPrincipalTest:Unregister", onUnregisterMessage);
--- a/browser/components/search/test/browser.ini +++ b/browser/components/search/test/browser.ini @@ -23,16 +23,17 @@ support-files = [browser_bing_behavior.js] [browser_contextmenu.js] [browser_contextSearchTabPosition.js] skip-if = os == "mac" # bug 967013 [browser_ddg.js] [browser_ddg_behavior.js] [browser_google.js] [browser_google_codes.js] +[browser_google_nocodes.js] [browser_google_behavior.js] [browser_healthreport.js] [browser_hiddenOneOffs_cleanup.js] [browser_hiddenOneOffs_diacritics.js] [browser_oneOffContextMenu.js] [browser_oneOffContextMenu_setDefault.js] [browser_oneOffHeader.js] [browser_private_search_perwindowpb.js]
--- a/browser/components/search/test/browser_google_codes.js +++ b/browser/components/search/test/browser_google_codes.js @@ -2,16 +2,18 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const kUrlPref = "geoSpecificDefaults.url"; const BROWSER_SEARCH_PREF = "browser.search."; var originalGeoURL; +var originalCountryCode; +var originalRegion; /** * Clean the profile of any cache file left from a previous run. * Returns a boolean indicating if the cache file existed. */ function removeCacheFile() { const CACHE_FILENAME = "search.json.mozlz4"; @@ -84,40 +86,42 @@ add_task(function* preparation() { gEngineCount = Services.search.getVisibleEngines().length; waitForSearchNotification("uninit-complete", () => { // Verify search service is not initialized is(Services.search.isInitialized, false, "Search service should NOT be initialized"); removeCacheFile(); + // Make sure we get the new country/region values, but save the old + originalCountryCode = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "countryCode"); + originalRegion = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "region"); + Services.prefs.clearUserPref(BROWSER_SEARCH_PREF + "countryCode"); + Services.prefs.clearUserPref(BROWSER_SEARCH_PREF + "region"); + // Geo specific defaults won't be fetched if there's no country code. Services.prefs.setCharPref("browser.search.geoip.url", - 'data:application/json,{"country_code": "US"}'); + 'data:application/json,{"country_code": "DE"}'); Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true); - // Make the new Google the only engine + // Avoid going to the server for the geo lookup. We take the defaults originalGeoURL = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + kUrlPref); - let geoUrl = 'data:application/json,{"interval": 31536000, "settings": {"searchDefault": "Google", "visibleDefaultEngines": ["google"]}}'; + let geoUrl = "data:application/json,{}"; Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF).setCharPref(kUrlPref, geoUrl); }); yield asyncReInit(); yield new Promise(resolve => { waitForSearchNotification("write-cache-to-disk-complete", resolve); }); }); add_task(function* tests() { - let engines = Services.search.getEngines(); - is(Services.search.currentEngine.name, "Google", "Search engine should be Google"); - is(engines.length, 1, "There should only be one engine"); - let engine = Services.search.getEngineByName("Google"); ok(engine, "Google"); let base = "https://www.google.com/search?q=foo&ie=utf-8&oe=utf-8&client=firefox-b"; // Keyword uses a slightly different code let keywordBase = base + "-ab"; @@ -143,16 +147,19 @@ add_task(function* cleanup() { waitForSearchNotification("uninit-complete", () => { // Verify search service is not initialized is(Services.search.isInitialized, false, "Search service should NOT be initialized"); removeCacheFile(); Services.prefs.clearUserPref("browser.search.geoip.url"); + Services.prefs.setCharPref(BROWSER_SEARCH_PREF + "countryCode", originalCountryCode) + Services.prefs.setCharPref(BROWSER_SEARCH_PREF + "region", originalRegion) + // We can't clear the pref because it's set to false by testing/profiles/prefs_general.js Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", false); Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF).setCharPref(kUrlPref, originalGeoURL); }); yield asyncReInit(); is(gEngineCount, Services.search.getVisibleEngines().length,
new file mode 100644 --- /dev/null +++ b/browser/components/search/test/browser_google_nocodes.js @@ -0,0 +1,164 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const kUrlPref = "geoSpecificDefaults.url"; +const BROWSER_SEARCH_PREF = "browser.search."; + +var originalGeoURL; +var originalCountryCode; +var originalRegion; + +/** + * Clean the profile of any cache file left from a previous run. + * Returns a boolean indicating if the cache file existed. + */ +function removeCacheFile() { + const CACHE_FILENAME = "search.json.mozlz4"; + + let file = Services.dirsvc.get("ProfD", Ci.nsIFile); + file.append(CACHE_FILENAME); + if (file.exists()) { + file.remove(false); + return true; + } + return false; +} + +/** + * Returns a promise that is resolved when an observer notification from the + * search service fires with the specified data. + * + * @param aExpectedData + * The value the observer notification sends that causes us to resolve + * the promise. + */ +function waitForSearchNotification(aExpectedData, aCallback) { + const SEARCH_SERVICE_TOPIC = "browser-search-service"; + Services.obs.addObserver(function observer(aSubject, aTopic, aData) { + if (aData != aExpectedData) + return; + + Services.obs.removeObserver(observer, SEARCH_SERVICE_TOPIC); + aCallback(); + }, SEARCH_SERVICE_TOPIC, false); +} + +function asyncInit() { + return new Promise(resolve => { + Services.search.init(function() { + ok(Services.search.isInitialized, "search service should be initialized"); + resolve(); + }); + }); +} + +function asyncReInit() { + const kLocalePref = "general.useragent.locale"; + + let promise = new Promise(resolve => { + waitForSearchNotification("reinit-complete", resolve); + }); + + Services.search.QueryInterface(Ci.nsIObserver) + .observe(null, "nsPref:changed", kLocalePref); + + return promise; +} + +let gEngineCount; + +add_task(function* preparation() { + // ContentSearch is interferring with our async re-initializations of the + // search service: once _initServicePromise has resolved, it will access + // the search service, thus causing unpredictable behavior due to + // synchronous initializations of the service. + let originalContentSearchPromise = ContentSearch._initServicePromise; + ContentSearch._initServicePromise = new Promise(resolve => { + registerCleanupFunction(() => { + ContentSearch._initServicePromise = originalContentSearchPromise; + resolve(); + }); + }); + + yield asyncInit(); + gEngineCount = Services.search.getVisibleEngines().length; + + waitForSearchNotification("uninit-complete", () => { + // Verify search service is not initialized + is(Services.search.isInitialized, false, "Search service should NOT be initialized"); + + removeCacheFile(); + + // Make sure we get the new country/region values, but save the old + originalCountryCode = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "countryCode"); + originalRegion = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "region"); + Services.prefs.clearUserPref(BROWSER_SEARCH_PREF + "countryCode"); + Services.prefs.clearUserPref(BROWSER_SEARCH_PREF + "region"); + + // Geo specific defaults won't be fetched if there's no country code. + Services.prefs.setCharPref("browser.search.geoip.url", + 'data:application/json,{"country_code": "US"}'); + + Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true); + + // Avoid going to the server for the geo lookup. We take the defaults + originalGeoURL = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + kUrlPref); + let geoUrl = "data:application/json,{}"; + Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF).setCharPref(kUrlPref, geoUrl); + }); + + yield asyncReInit(); + + yield new Promise(resolve => { + waitForSearchNotification("write-cache-to-disk-complete", resolve); + }); +}); + +add_task(function* tests() { + let engine = Services.search.getEngineByName("Google"); + ok(engine, "Google"); + + let base = "https://www.google.com/search?q=foo&ie=utf-8&oe=utf-8"; + + let url; + + // Test search URLs (including purposes). + url = engine.getSubmission("foo", null, "contextmenu").uri.spec; + is(url, base, "Check context menu search URL for 'foo'"); + url = engine.getSubmission("foo", null, "keyword").uri.spec; + is(url, base, "Check keyword search URL for 'foo'"); + url = engine.getSubmission("foo", null, "searchbar").uri.spec; + is(url, base, "Check search bar search URL for 'foo'"); + url = engine.getSubmission("foo", null, "homepage").uri.spec; + is(url, base, "Check homepage search URL for 'foo'"); + url = engine.getSubmission("foo", null, "newtab").uri.spec; + is(url, base, "Check newtab search URL for 'foo'"); + url = engine.getSubmission("foo", null, "system").uri.spec; + is(url, base, "Check system search URL for 'foo'"); +}); + + +add_task(function* cleanup() { + waitForSearchNotification("uninit-complete", () => { + // Verify search service is not initialized + is(Services.search.isInitialized, false, + "Search service should NOT be initialized"); + removeCacheFile(); + + Services.prefs.clearUserPref("browser.search.geoip.url"); + + Services.prefs.setCharPref(BROWSER_SEARCH_PREF + "countryCode", originalCountryCode) + Services.prefs.setCharPref(BROWSER_SEARCH_PREF + "region", originalRegion) + + // We can't clear the pref because it's set to false by testing/profiles/prefs_general.js + Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", false); + + Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF).setCharPref(kUrlPref, originalGeoURL); + }); + + yield asyncReInit(); + is(gEngineCount, Services.search.getVisibleEngines().length, + "correct engine count after cleanup"); +});
--- a/browser/extensions/webcompat-reporter/moz.build +++ b/browser/extensions/webcompat-reporter/moz.build @@ -15,8 +15,11 @@ FINAL_TARGET_FILES.features['webcompat-r FINAL_TARGET_PP_FILES.features['webcompat-reporter@mozilla.org'] += [ 'install.rdf.in' ] JAR_MANIFESTS += ['jar.mn'] BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini'] + +with Files('**'): + BUG_COMPONENT = ('Web Compatibility', 'General')
--- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -813,21 +813,16 @@ userContextOpenLink.label = Open Link in muteTab.label = Mute Tab muteTab.accesskey = M unmuteTab.label = Unmute Tab unmuteTab.accesskey = m playTab.label = Play Tab playTab.accesskey = l -# LOCALIZATION NOTE (weakCryptoOverriding.message): %S is brandShortName -weakCryptoOverriding.message = %S recommends that you don’t enter your password, credit card and other personal information on this website. -revokeOverride.label = Don’t Trust This Website -revokeOverride.accesskey = D - # LOCALIZATION NOTE (certErrorDetails*.label): These are text strings that # appear in the about:certerror page, so that the user can copy and send them to # the server administrators for troubleshooting. certErrorDetailsHSTS.label = HTTP Strict Transport Security: %S certErrorDetailsKeyPinning.label = HTTP Public Key Pinning: %S certErrorDetailsCertChain.label = Certificate chain: # LOCALIZATION NOTE (pendingCrashReports2.label): Semi-colon list of plural forms
--- a/browser/locales/en-US/chrome/overrides/appstrings.properties +++ b/browser/locales/en-US/chrome/overrides/appstrings.properties @@ -33,11 +33,9 @@ externalProtocolLaunchBtn=Launch applica malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences. unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences. deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences. cspBlocked=This page has a content security policy that prevents it from being loaded in this way. corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired. remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox. ## LOCALIZATION NOTE (sslv3Used) - Do not translate "%S". sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol. -## LOCALIZATION NOTE (weakCryptoUsed) - Do not translate "%S". -weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website. inadequateSecurityError=The website tried to negotiate an inadequate level of security.
--- a/browser/locales/en-US/chrome/overrides/netError.dtd +++ b/browser/locales/en-US/chrome/overrides/netError.dtd @@ -184,24 +184,16 @@ was trying to connect. --> <!ENTITY remoteXUL.title "Remote XUL"> <!ENTITY remoteXUL.longDesc "<p><ul><li>Please contact the website owners to inform them of this problem.</li></ul></p>"> <!ENTITY sslv3Used.title "Unable to Connect Securely"> <!-- LOCALIZATION NOTE (sslv3Used.longDesc2) - Do not translate "SSL_ERROR_UNSUPPORTED_VERSION". --> <!ENTITY sslv3Used.longDesc2 "Advanced info: SSL_ERROR_UNSUPPORTED_VERSION"> -<!ENTITY weakCryptoUsed.title "Your connection is not secure"> -<!-- LOCALIZATION NOTE (weakCryptoUsed.longDesc2) - Do not translate - "SSL_ERROR_NO_CYPHER_OVERLAP". --> -<!ENTITY weakCryptoUsed.longDesc2 "Advanced info: SSL_ERROR_NO_CYPHER_OVERLAP"> -<!ENTITY weakCryptoAdvanced.title "Advanced"> -<!ENTITY weakCryptoAdvanced.longDesc "<span class='hostname'></span> uses security technology that is outdated and vulnerable to attack. An attacker could easily reveal information which you thought to be safe."> -<!ENTITY weakCryptoAdvanced.override "(Not secure) Try loading <span class='hostname'></span> using outdated security"> - <!-- LOCALIZATION NOTE (certerror.wrongSystemTime2, certerror.wrongSystemTimeWithoutReference) - The <span id='..' /> tags will be injected with actual values, please leave them unchanged. --> <!ENTITY certerror.wrongSystemTime2 "<p> &brandShortName; did not connect to <span id='wrongSystemTime_URL'/> because your computer’s clock appears to show the wrong time and this is preventing a secure connection.</p> <p>Your computer is set to <span id='wrongSystemTime_systemDate'/>, when it should be <span id='wrongSystemTime_actualDate'/>. To fix this problem, change your date and time settings to match the correct time.</p>"> <!ENTITY certerror.wrongSystemTimeWithoutReference "<p>&brandShortName; did not connect to <span id='wrongSystemTimeWithoutReference_URL'/> because your computer’s clock appears to show the wrong time and this is preventing a secure connection.</p> <p>Your computer is set to <span id='wrongSystemTimeWithoutReference_systemDate'/>. To fix this problem, change your date and time settings to match the correct time.</p>"> <!ENTITY certerror.pagetitle1 "Insecure Connection"> <!ENTITY certerror.whatShouldIDo.badStsCertExplanation "This site uses HTTP
--- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -727,24 +727,58 @@ nsScriptSecurityManager::CheckLoadURIWit if (NS_FAILED(rv)) return rv; while (currentURI && currentOtherURI) { nsAutoCString scheme, otherScheme; currentURI->GetScheme(scheme); currentOtherURI->GetScheme(otherScheme); bool schemesMatch = scheme.Equals(otherScheme, stringComparator); - bool isSamePage; + bool isSamePage = false; // about: URIs are special snowflakes. - if (scheme.EqualsLiteral("about")) { - nsAutoCString module, otherModule; - isSamePage = schemesMatch && - NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, module)) && - NS_SUCCEEDED(NS_GetAboutModuleName(currentOtherURI, otherModule)) && - module.Equals(otherModule); + if (scheme.EqualsLiteral("about") && schemesMatch) { + nsAutoCString moduleName, otherModuleName; + // about: pages can always link to themselves: + isSamePage = + NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, moduleName)) && + NS_SUCCEEDED(NS_GetAboutModuleName(currentOtherURI, otherModuleName)) && + moduleName.Equals(otherModuleName); + if (!isSamePage) { + // We will have allowed the load earlier if the source page has + // system principal. So we know the source has a content + // principal, and it's trying to link to something else. + // Linkable about: pages are always reachable, even if we hit + // the CheckLoadURIFlags call below. + // We punch only 1 other hole: iff the source is unlinkable, + // we let them link to other pages explicitly marked SAFE + // for content. This avoids world-linkable about: pages linking + // to non-world-linkable about: pages. + nsCOMPtr<nsIAboutModule> module, otherModule; + bool knowBothModules = + NS_SUCCEEDED(NS_GetAboutModule(currentURI, getter_AddRefs(module))) && + NS_SUCCEEDED(NS_GetAboutModule(currentOtherURI, getter_AddRefs(otherModule))); + uint32_t aboutModuleFlags = 0; + uint32_t otherAboutModuleFlags = 0; + knowBothModules = knowBothModules && + NS_SUCCEEDED(module->GetURIFlags(currentURI, &aboutModuleFlags)) && + NS_SUCCEEDED(otherModule->GetURIFlags(currentOtherURI, &otherAboutModuleFlags)); + if (knowBothModules) { + isSamePage = + !(aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) && + (otherAboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT); + if (isSamePage && otherAboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) { + //XXXgijs: this is a hack. The target will be nested + // (with innerURI of moz-safe-about:whatever), and + // the source isn't, so we won't pass if we finish + // the loop. We *should* pass, though, so return here. + // This hack can go away when bug 1228118 is fixed. + return NS_OK; + } + } + } } else { bool equalExceptRef = false; rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef); isSamePage = NS_SUCCEEDED(rv) && equalExceptRef; } // If schemes are not equal, or they're equal but the target URI // is different from the source URI and doesn't always allow linking
--- a/caps/tests/mochitest/browser_checkloaduri.js +++ b/caps/tests/mochitest/browser_checkloaduri.js @@ -1,11 +1,48 @@ "use strict"; let ssm = Services.scriptSecurityManager; +// This will show a directory listing, but we never actually load these so that's OK. +const kDummyPage = getRootDirectory(gTestPath); + +const kAboutPagesRegistered = Promise.all([ + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-chrome-privs", kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-chrome-privs2", kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-unknown-linkable", kDummyPage, + Ci.nsIAboutModule.MAKE_LINKABLE | Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-unknown-linkable2", kDummyPage, + Ci.nsIAboutModule.MAKE_LINKABLE | Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-unknown-unlinkable", kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-unknown-unlinkable2", kDummyPage, + Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-content-unlinkable", kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-content-unlinkable2", kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-content-linkable", kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.MAKE_LINKABLE | + Ci.nsIAboutModule.ALLOW_SCRIPT), + BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "test-content-linkable2", kDummyPage, + Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.MAKE_LINKABLE | + Ci.nsIAboutModule.ALLOW_SCRIPT), +]); const URLs = new Map([ ["http://www.example.com", [ // For each of these entries, the booleans represent whether the parent URI can: // - load them // - load them without principal inheritance // - whether the URI can be created at all (some protocol handlers will // refuse to create certain variants) @@ -17,65 +54,171 @@ const URLs = new Map([ ["view-source:http://www.example2.com", false, false, true], ["view-source:https://www.example2.com", false, false, true], ["view-source:feed:http://www.example2.com", false, false, true], ["feed:view-source:http://www.example2.com", false, false, false], ["data:text/html,Hi", true, false, true], ["view-source:data:text/html,Hi", false, false, true], ["javascript:alert('hi')", true, false, true], ["moz://a", false, false, true], + ["about:test-chrome-privs", false, false, true], + ["about:test-unknown-unlinkable", false, false, true], + ["about:test-content-unlinkable", false, false, true], + ["about:test-content-linkable", true, true, true], + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], ]], ["feed:http://www.example.com", [ ["http://www.example2.com", true, true, true], ["feed:http://www.example2.com", true, true, true], ["https://www.example2.com", true, true, true], ["feed:https://www.example2.com", true, true, true], ["chrome://foo/content/bar.xul", false, false, true], ["feed:chrome://foo/content/bar.xul", false, false, false], ["view-source:http://www.example2.com", false, false, true], ["view-source:https://www.example2.com", false, false, true], ["view-source:feed:http://www.example2.com", false, false, true], ["feed:view-source:http://www.example2.com", false, false, false], ["data:text/html,Hi", true, false, true], ["view-source:data:text/html,Hi", false, false, true], ["javascript:alert('hi')", true, false, true], ["moz://a", false, false, true], + ["about:test-chrome-privs", false, false, true], + ["about:test-unknown-unlinkable", false, false, true], + ["about:test-content-unlinkable", false, false, true], + ["about:test-content-linkable", true, true, true], + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], ]], ["view-source:http://www.example.com", [ ["http://www.example2.com", true, true, true], ["feed:http://www.example2.com", false, false, true], ["https://www.example2.com", true, true, true], ["feed:https://www.example2.com", false, false, true], ["chrome://foo/content/bar.xul", false, false, true], ["feed:chrome://foo/content/bar.xul", false, false, false], ["view-source:http://www.example2.com", true, true, true], ["view-source:https://www.example2.com", true, true, true], ["view-source:feed:http://www.example2.com", false, false, true], ["feed:view-source:http://www.example2.com", false, false, false], ["data:text/html,Hi", true, false, true], ["view-source:data:text/html,Hi", true, false, true], ["javascript:alert('hi')", true, false, true], ["moz://a", false, false, true], + ["about:test-chrome-privs", false, false, true], + ["about:test-unknown-unlinkable", false, false, true], + ["about:test-content-unlinkable", false, false, true], + ["about:test-content-linkable", true, true, true], + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], ]], - ["about:foo", [ - ["about:foo?", true, true, true], - ["about:foo?bar", true, true, true], - ["about:foo#", true, true, true], - ["about:foo#bar", true, true, true], - ["about:foo?#", true, true, true], - ["about:foo?bar#baz", true, true, true], - ["about:bar", false, false, true], - ["about:bar?foo#baz", false, false, true], - ["about:bar?foo", false, false, true], - ["http://www.example.com/", true, true, true], - ["moz://a", false, false, true], + // about: related tests. + ["about:test-chrome-privs", [ + ["about:test-chrome-privs", true, true, true], + ["about:test-chrome-privs2", true, true, true], + ["about:test-chrome-privs2?foo#bar", true, true, true], + ["about:test-chrome-privs2?foo", true, true, true], + ["about:test-chrome-privs2#bar", true, true, true], + + ["about:test-unknown-unlinkable", true, true, true], + + ["about:test-content-unlinkable", true, true, true], + ["about:test-content-unlinkable?foo", true, true, true], + ["about:test-content-unlinkable?foo#bar", true, true, true], + ["about:test-content-unlinkable#bar", true, true, true], + + ["about:test-content-linkable", true, true, true], + + ["about:test-unknown-linkable", true, true, true], + ]], + ["about:test-unknown-unlinkable", [ + ["about:test-chrome-privs", false, false, true], + + // Can link to ourselves: + ["about:test-unknown-unlinkable", true, true, true], + // Can't link to unlinkable content if we're not sure it's privileged: + ["about:test-unknown-unlinkable2", false, false, true], + + ["about:test-content-unlinkable", true, true, true], + ["about:test-content-unlinkable2", true, true, true], + ["about:test-content-unlinkable2?foo", true, true, true], + ["about:test-content-unlinkable2?foo#bar", true, true, true], + ["about:test-content-unlinkable2#bar", true, true, true], + + ["about:test-content-linkable", true, true, true], + + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], + ]], + ["about:test-content-unlinkable", [ + ["about:test-chrome-privs", false, false, true], + + // Can't link to unlinkable content if we're not sure it's privileged: + ["about:test-unknown-unlinkable", false, false, true], + + ["about:test-content-unlinkable", true, true, true], + ["about:test-content-unlinkable2", true, true, true], + ["about:test-content-unlinkable2?foo", true, true, true], + ["about:test-content-unlinkable2?foo#bar", true, true, true], + ["about:test-content-unlinkable2#bar", true, true, true], + + ["about:test-content-linkable", true, true, true], + ["about:test-unknown-linkable", false, false, true], + ]], + ["about:test-unknown-linkable", [ + ["about:test-chrome-privs", false, false, true], + + // Linkable content can't link to unlinkable content. + ["about:test-unknown-unlinkable", false, false, true], + + ["about:test-content-unlinkable", false, false, true], + ["about:test-content-unlinkable2", false, false, true], + ["about:test-content-unlinkable2?foo", false, false, true], + ["about:test-content-unlinkable2?foo#bar", false, false, true], + ["about:test-content-unlinkable2#bar", false, false, true], + + // ... but it can link to other linkable content. + ["about:test-content-linkable", true, true, true], + + // Can link to ourselves: + ["about:test-unknown-linkable", true, true, true], + + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable2", false, false, true], + ]], + ["about:test-content-linkable", [ + ["about:test-chrome-privs", false, false, true], + + // Linkable content can't link to unlinkable content. + ["about:test-unknown-unlinkable", false, false, true], + + ["about:test-content-unlinkable", false, false, true], + + // ... but it can link to itself and other linkable content. + ["about:test-content-linkable", true, true, true], + ["about:test-content-linkable2", true, true, true], + + // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it: + ["about:test-unknown-linkable", false, false, true], ]], ]); function testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, flags) { + function getPrincipalDesc(principal) { + if (principal.URI) { + return principal.URI.spec; + } + if (principal.isSystemPrincipal) { + return "system principal"; + } + if (principal.isNullPrincipal) { + return "null principal"; + } + return "unknown principal"; + } let threw = false; let targetURI; try { targetURI = makeURI(target); } catch (ex) { ok(!canCreate, "Shouldn't be passing URIs that we can't create. Failed to create: " + target); return; } @@ -86,24 +229,30 @@ function testURL(source, target, canLoad } catch (ex) { info(ex.message); threw = true; } let inheritDisallowed = flags & ssm.DISALLOW_INHERIT_PRINCIPAL; let shouldThrow = inheritDisallowed ? !canLoadWithoutInherit : !canLoad; ok(threw == shouldThrow, "Should " + (shouldThrow ? "" : "not ") + "throw an error when loading " + - target + " from " + source.URI.spec + + target + " from " + getPrincipalDesc(source) + (inheritDisallowed ? " without" : " with") + " principal inheritance."); } add_task(function* () { + yield kAboutPagesRegistered; let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS; for (let [sourceString, targetsAndExpectations] of URLs) { - let source = ssm.createCodebasePrincipal(makeURI(sourceString), {}); + let source; + if (sourceString.startsWith("about:test-chrome-privs")) { + source = ssm.getSystemPrincipal(); + } else { + source = ssm.createCodebasePrincipal(makeURI(sourceString), {}); + } for (let [target, canLoad, canLoadWithoutInherit, canCreate] of targetsAndExpectations) { testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, baseFlags); testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL); } } // Now test blob URIs, which we need to do in-content.
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -14249,29 +14249,27 @@ nsDocShell::ShouldBlockLoadingForBackBut bool canGoForward = false; GetCanGoForward(&canGoForward); return canGoForward; } bool nsDocShell::PluginsAllowedInCurrentDoc() { - bool pluginsAllowed = false; if (!mContentViewer) { return false; } nsIDocument* doc = mContentViewer->GetDocument(); if (!doc) { return false; } - doc->GetAllowPlugins(&pluginsAllowed); - return pluginsAllowed; + return doc->GetAllowPlugins(); } //---------------------------------------------------------------------- // Web Shell Services API // This functions is only called when a new charset is detected in loading a // document. Its name should be changed to "CharsetReloadDocument" NS_IMETHODIMP
--- a/dom/animation/ComputedTimingFunction.cpp +++ b/dom/animation/ComputedTimingFunction.cpp @@ -23,43 +23,48 @@ ComputedTimingFunction::Init(const nsTim } static inline double StepTiming(uint32_t aSteps, double aPortion, ComputedTimingFunction::BeforeFlag aBeforeFlag, nsTimingFunction::Type aType) { - MOZ_ASSERT(0.0 <= aPortion && aPortion <= 1.0, "out of range"); MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart || aType == nsTimingFunction::Type::StepEnd, "invalid type"); - if (aPortion == 1.0) { - return 1.0; - } - // Calculate current step using step-end behavior - uint32_t step = uint32_t(aPortion * aSteps); // floor + int32_t step = floor(aPortion * aSteps); // step-start is one step ahead if (aType == nsTimingFunction::Type::StepStart) { step++; } // If the "before flag" is set and we are at a transition point, - // drop back a step (but only if we are not already at the zero point-- - // we do this clamping here since |step| is an unsigned integer) - if (step != 0 && - aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set && + // drop back a step + if (aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set && fmod(aPortion * aSteps, 1) == 0) { step--; } // Convert to a progress value - return double(step) / double(aSteps); + double result = double(step) / double(aSteps); + + // We should not produce a result outside [0, 1] unless we have an + // input outside that range. This takes care of steps that would otherwise + // occur at boundaries. + if (result < 0.0 && aPortion >= 0.0) { + return 0.0; + } + if (result > 1.0 && aPortion <= 1.0) { + return 1.0; + } + + return result; } double ComputedTimingFunction::GetValue( double aPortion, ComputedTimingFunction::BeforeFlag aBeforeFlag) const { if (HasSpline()) { @@ -103,28 +108,16 @@ ComputedTimingFunction::GetValue( } // If we can't calculate a sensible tangent, don't extrapolate at all. return 1.0; } return mTimingFunction.GetSplineValue(aPortion); } - // Since we use endpoint-exclusive timing, the output of a steps(start) timing - // function when aPortion = 0.0 is the top of the first step. When aPortion is - // negative, however, we should use the bottom of the first step. We handle - // negative values of aPortion specially here since once we clamp aPortion - // to [0,1] below we will no longer be able to distinguish to the two cases. - if (aPortion < 0.0) { - return 0.0; - } - - // Clamp in case of steps(end) and steps(start) for values greater than 1. - aPortion = clamped(aPortion, 0.0, 1.0); - return StepTiming(mSteps, aPortion, aBeforeFlag, mType); } int32_t ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const { if (mType != aRhs.mType) { return int32_t(mType) - int32_t(aRhs.mType);
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3139,37 +3139,42 @@ nsDocument::GetContentType(nsAString& aC } void nsDocument::SetContentType(const nsAString& aContentType) { SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType)); } -nsresult -nsDocument::GetAllowPlugins(bool * aAllowPlugins) +bool +nsDocument::GetAllowPlugins() { // First, we ask our docshell if it allows plugins. nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); if (docShell) { - docShell->GetAllowPlugins(aAllowPlugins); + bool allowPlugins = false; + docShell->GetAllowPlugins(&allowPlugins); + if (!allowPlugins) { + return false; + } // If the docshell allows plugins, we check whether // we are sandboxed and plugins should not be allowed. - if (*aAllowPlugins) - *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS); - } - - if (*aAllowPlugins) { - FlashClassification classification = DocumentFlashClassification(); - *aAllowPlugins = (classification != FlashClassification::Denied); - } - - return NS_OK; + if (mSandboxFlags & SANDBOXED_PLUGINS) { + return false; + } + } + + FlashClassification classification = DocumentFlashClassification(); + if (classification == FlashClassification::Denied) { + return false; + } + + return true; } bool nsDocument::IsElementAnimateEnabled(JSContext* aCx, JSObject* /*unused*/) { MOZ_ASSERT(NS_IsMainThread()); return nsContentUtils::IsSystemCaller(aCx) || @@ -13136,16 +13141,19 @@ nsDocument::ComputeFlashClassification() "nsIDocShellTreeItem::GetSameTypeParent should never fail"); bool isTopLevel = !parent; FlashClassification classification; if (isTopLevel) { classification = PrincipalFlashClassification(isTopLevel); } else { nsCOMPtr<nsIDocument> parentDocument = GetParentDocument(); + if (!parentDocument) { + return FlashClassification::Denied; + } FlashClassification parentClassification = parentDocument->DocumentFlashClassification(); if (parentClassification == FlashClassification::Denied) { classification = FlashClassification::Denied; } else { classification = PrincipalFlashClassification(isTopLevel);
--- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -594,17 +594,17 @@ public: * shared among multiple presentation shells). */ already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext, nsViewManager* aViewManager, mozilla::StyleSetHandle aStyleSet) final; virtual void DeleteShell() override; - virtual nsresult GetAllowPlugins(bool* aAllowPlugins) override; + virtual bool GetAllowPlugins() override; static bool IsElementAnimateEnabled(JSContext* aCx, JSObject* aObject); static bool IsWebAnimationsEnabled(JSContext* aCx, JSObject* aObject); virtual mozilla::dom::DocumentTimeline* Timeline() override; virtual void GetAnimations( nsTArray<RefPtr<mozilla::dom::Animation>>& aAnimations) override; mozilla::LinkedList<mozilla::dom::DocumentTimeline>& Timelines() override {
--- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -876,17 +876,17 @@ public: void SetParentDocument(nsIDocument* aParent) { mParentDocument = aParent; } /** * Are plugins allowed in this document ? */ - virtual nsresult GetAllowPlugins (bool* aAllowPlugins) = 0; + virtual bool GetAllowPlugins () = 0; /** * Set the sub document for aContent to aSubDoc. */ virtual nsresult SetSubDocumentFor(Element* aContent, nsIDocument* aSubDoc) = 0; /**
--- a/dom/base/nsPluginArray.cpp +++ b/dom/base/nsPluginArray.cpp @@ -306,19 +306,25 @@ nsPluginArray::Observe(nsISupports *aSub } return NS_OK; } bool nsPluginArray::AllowPlugins() const { - nsCOMPtr<nsIDocShell> docShell = mWindow ? mWindow->GetDocShell() : nullptr; + if (!mWindow) { + return false; + } + nsCOMPtr<nsIDocument> doc = mWindow->GetDoc(); + if (!doc) { + return false; + } - return docShell && docShell->PluginsAllowedInCurrentDoc(); + return doc->GetAllowPlugins(); } static bool operator<(const RefPtr<nsPluginElement>& lhs, const RefPtr<nsPluginElement>& rhs) { // Sort plugins alphabetically by name. return lhs->PluginTag()->Name() < rhs->PluginTag()->Name();
--- a/dom/base/test/file_use_counter_svg_currentScale.svg +++ b/dom/base/test/file_use_counter_svg_currentScale.svg @@ -1,12 +1,9 @@ <?xml version="1.0" standalone="no"?> - -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="4in" height="3in" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <desc>Test graphic for hitting currentScale </desc> <script type="text/javascript"> <![CDATA[ document.documentElement.currentScale = document.documentElement.currentScale; ]]>
--- a/dom/base/test/file_use_counter_svg_fill_pattern.svg +++ b/dom/base/test/file_use_counter_svg_fill_pattern.svg @@ -1,11 +1,9 @@ <?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1" xmlns="http://www.w3.org/2000/svg"> <desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc> <!-- Outline the drawing area in blue --> <rect fill="none" stroke="blue" x="1" y="1" width="798" height="398"/> <!-- The ellipse is filled using a triangle pattern paint server
--- a/dom/base/test/file_use_counter_svg_fill_pattern_data.svg +++ b/dom/base/test/file_use_counter_svg_fill_pattern_data.svg @@ -1,11 +1,9 @@ <?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1" xmlns="http://www.w3.org/2000/svg"> <desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc> <!-- Outline the drawing area in blue --> <rect fill="none" stroke="blue" x="1" y="1" width="798" height="398"/> <!-- The ellipse is filled using a triangle pattern paint server
--- a/dom/base/test/file_use_counter_svg_fill_pattern_definition.svg +++ b/dom/base/test/file_use_counter_svg_fill_pattern_definition.svg @@ -1,11 +1,9 @@ <?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1" xmlns="http://www.w3.org/2000/svg"> <desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc> <defs> <pattern id="TrianglePattern" patternUnits="userSpaceOnUse" x="0" y="0" width="100" height="100" viewBox="0 0 10 10" > <path d="M 0 0 L 7 0 L 3.5 7 z" fill="red" fill-opacity="0.7" stroke="blue" />
--- a/dom/base/test/file_use_counter_svg_fill_pattern_internal.svg +++ b/dom/base/test/file_use_counter_svg_fill_pattern_internal.svg @@ -1,11 +1,9 @@ <?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1" xmlns="http://www.w3.org/2000/svg"> <desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc> <!-- Outline the drawing area in blue --> <rect fill="none" stroke="blue" x="1" y="1" width="798" height="398"/> <defs>
--- a/dom/base/test/file_use_counter_svg_getElementById.svg +++ b/dom/base/test/file_use_counter_svg_getElementById.svg @@ -1,12 +1,9 @@ <?xml version="1.0" standalone="no"?> - -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="4in" height="3in" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <desc>Test graphic for hitting getElementById </desc> <image id="i1" x="200" y="200" width="100px" height="80px"> </image> <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
--- a/dom/base/test/w3element_traversal.svg +++ b/dom/base/test/w3element_traversal.svg @@ -1,9 +1,9 @@ -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" +<!DOCTYPE svg [ <!ENTITY tree "<tspan id='first_element_child_entity'></tspan>"> ]> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:pickle="http://ns.example.org/pickle" version="1.1"
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -7126,64 +7126,110 @@ HTMLMediaElement::MarkAsContentSource(Ca const bool isVisible = mVisibilityState != Visibility::APPROXIMATELY_NONVISIBLE; if (isVisible) { // 0 = ALL_VISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 0); } else { // 1 = ALL_INVISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 1); + + if (IsInUncomposedDoc()) { + // 0 = ALL_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 0); + } else { + // 1 = ALL_NOT_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 1); + } } switch (aAPI) { case CallerAPI::DRAW_IMAGE: { if (isVisible) { // 2 = drawImage_VISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 2); } else { // 3 = drawImage_INVISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 3); + + if (IsInUncomposedDoc()) { + // 2 = drawImage_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 2); + } else { + // 3 = drawImage_NOT_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 3); + } } break; } case CallerAPI::CREATE_PATTERN: { if (isVisible) { // 4 = createPattern_VISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 4); } else { // 5 = createPattern_INVISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 5); + + if (IsInUncomposedDoc()) { + // 4 = createPattern_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 4); + } else { + // 5 = createPattern_NOT_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 5); + } } break; } case CallerAPI::CREATE_IMAGEBITMAP: { if (isVisible) { // 6 = createImageBitmap_VISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 6); } else { // 7 = createImageBitmap_INVISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 7); + + if (IsInUncomposedDoc()) { + // 6 = createImageBitmap_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 6); + } else { + // 7 = createImageBitmap_NOT_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 7); + } } break; } case CallerAPI::CAPTURE_STREAM: { if (isVisible) { // 8 = captureStream_VISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 8); } else { // 9 = captureStream_INVISIBLE Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 9); + + if (IsInUncomposedDoc()) { + // 8 = captureStream_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 8); + } else { + // 9 = captureStream_NOT_IN_TREE + Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 9); + } } break; } } LOG(LogLevel::Debug, ("%p Log VIDEO_AS_CONTENT_SOURCE: visibility = %u, API: '%d' and 'All'", this, isVisible, aAPI)); + + if (!isVisible) { + LOG(LogLevel::Debug, + ("%p Log VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT: inTree = %u, API: '%d' and 'All'", + this, IsInUncomposedDoc(), aAPI)); + } } void HTMLMediaElement::UpdateCustomPolicyAfterPlayed() { OpenUnsupportedMediaWithExternalAppIfNeeded(); if (mAudioChannelWrapper) { mAudioChannelWrapper->NotifyPlayStateChanged();
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -183,16 +183,17 @@ #include "nsHostObjectProtocolHandler.h" #include "nsICaptivePortalService.h" #include "nsIBidiKeyboard.h" #include "nsLayoutStylesheetCache.h" #include "ContentPrefs.h" +#include "mozilla/Sprintf.h" #ifdef MOZ_WEBRTC #include "signaling/src/peerconnection/WebrtcGlobalParent.h" #endif #if defined(ANDROID) || defined(LINUX) #include "nsSystemInfo.h" #endif
--- a/dom/media/WebVTTListener.cpp +++ b/dom/media/WebVTTListener.cpp @@ -6,16 +6,17 @@ #include "WebVTTListener.h" #include "mozilla/dom/TextTrackCue.h" #include "mozilla/dom/TextTrackRegion.h" #include "mozilla/dom/VTTRegionBinding.h" #include "mozilla/dom/HTMLTrackElement.h" #include "nsIInputStream.h" #include "nsIWebVTTParserWrapper.h" #include "nsComponentManagerUtils.h" +#include "nsIAsyncVerifyRedirectCallback.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION(WebVTTListener, mElement, mParserWrapper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebVTTListener) NS_INTERFACE_MAP_ENTRY(nsIWebVTTListener) @@ -72,16 +73,17 @@ NS_IMETHODIMP WebVTTListener::AsyncOnChannelRedirect(nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags, nsIAsyncVerifyRedirectCallback* cb) { if (mElement) { mElement->OnChannelRedirect(aOldChannel, aNewChannel, aFlags); } + cb->OnRedirectVerifyCallback(NS_OK); return NS_OK; } NS_IMETHODIMP WebVTTListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { VTT_LOG("WebVTTListener::OnStartRequest\n");
--- a/dom/xslt/xslt/txXPathResultComparator.cpp +++ b/dom/xslt/xslt/txXPathResultComparator.cpp @@ -4,18 +4,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/FloatingPoint.h" #include "txXPathResultComparator.h" #include "txExpr.h" #include "txCore.h" #include "nsCollationCID.h" -#include "nsILocale.h" -#include "nsILocaleService.h" #include "nsIServiceManager.h" #include "prmem.h" #define kAscending (1<<0) #define kUpperFirst (1<<1) txResultStringComparator::txResultStringComparator(bool aAscending, bool aUpperFirst, @@ -30,35 +28,26 @@ txResultStringComparator::txResultString if (NS_FAILED(rv)) NS_ERROR("Failed to initialize txResultStringComparator"); } nsresult txResultStringComparator::init(const nsAFlatString& aLanguage) { nsresult rv; - nsCOMPtr<nsILocaleService> localeService = - do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsILocale> locale; - if (!aLanguage.IsEmpty()) { - rv = localeService->NewLocale(aLanguage, - getter_AddRefs(locale)); - } - else { - rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); - } - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsICollationFactory> colFactory = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation)); + if (aLanguage.IsEmpty()) { + rv = colFactory->CreateCollation(getter_AddRefs(mCollation)); + } else { + rv = colFactory->CreateCollationForLocale(NS_ConvertUTF16toUTF8(aLanguage), getter_AddRefs(mCollation)); + } + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult txResultStringComparator::createSortableValue(Expr *aExpr, txIEvalContext *aContext,
--- a/dom/xul/templates/nsXULContentUtils.cpp +++ b/dom/xul/templates/nsXULContentUtils.cpp @@ -47,18 +47,16 @@ #include "nsGkAtoms.h" #include "mozilla/Logging.h" #include "prtime.h" #include "rdf.h" #include "nsContentUtils.h" #include "nsIScriptableDateFormat.h" #include "nsICollation.h" #include "nsCollationCID.h" -#include "nsILocale.h" -#include "nsILocaleService.h" #include "nsIConsoleService.h" #include "nsEscape.h" using namespace mozilla; //------------------------------------------------------------------------ nsIRDFService* nsXULContentUtils::gRDF; @@ -120,37 +118,24 @@ nsXULContentUtils::Finish() return NS_OK; } nsICollation* nsXULContentUtils::GetCollation() { if (!gCollation) { - nsresult rv; - - // get a locale service - nsCOMPtr<nsILocaleService> localeService = - do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr<nsILocale> locale; - rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); - if (NS_SUCCEEDED(rv) && locale) { - nsCOMPtr<nsICollationFactory> colFactory = - do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); - if (colFactory) { - rv = colFactory->CreateCollation(locale, &gCollation); - NS_ASSERTION(NS_SUCCEEDED(rv), - "couldn't create collation instance"); - } else - NS_ERROR("couldn't create instance of collation factory"); - } else - NS_ERROR("unable to get application locale"); + nsCOMPtr<nsICollationFactory> colFactory = + do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); + if (colFactory) { + DebugOnly<nsresult> rv = colFactory->CreateCollation(&gCollation); + NS_ASSERTION(NS_SUCCEEDED(rv), + "couldn't create collation instance"); } else - NS_ERROR("couldn't get locale factory"); + NS_ERROR("couldn't create instance of collation factory"); } return gCollation; } //------------------------------------------------------------------------ nsresult
--- a/intl/locale/mac/nsCollationMacUC.cpp +++ b/intl/locale/mac/nsCollationMacUC.cpp @@ -1,42 +1,36 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsCollationMacUC.h" -#include "nsILocaleService.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsIServiceManager.h" #include "prmem.h" #include "nsString.h" NS_IMPL_ISUPPORTS(nsCollationMacUC, nsICollation) nsCollationMacUC::nsCollationMacUC() : mInit(false) , mHasCollator(false) - , mLocaleICU(nullptr) , mLastStrength(-1) , mCollatorICU(nullptr) { } nsCollationMacUC::~nsCollationMacUC() { #ifdef DEBUG nsresult res = #endif CleanUpCollator(); NS_ASSERTION(NS_SUCCEEDED(res), "CleanUpCollator failed"); - if (mLocaleICU) { - free(mLocaleICU); - mLocaleICU = nullptr; - } } nsresult nsCollationMacUC::ConvertStrength(const int32_t aNSStrength, UCollationStrength* aICUStrength, UColAttributeValue* aCaseLevelOut) { NS_ENSURE_ARG_POINTER(aICUStrength); NS_ENSURE_TRUE((aNSStrength < 4), NS_ERROR_FAILURE); @@ -63,53 +57,29 @@ nsresult nsCollationMacUC::ConvertStreng } *aICUStrength = strength; *aCaseLevelOut = caseLevel; return NS_OK; } -nsresult nsCollationMacUC::ConvertLocaleICU(nsILocale* aNSLocale, char** aICULocale) -{ - NS_ENSURE_ARG_POINTER(aNSLocale); - NS_ENSURE_ARG_POINTER(aICULocale); - - nsAutoString localeString; - nsresult res = aNSLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_COLLATE"), localeString); - NS_ENSURE_TRUE(NS_SUCCEEDED(res) && !localeString.IsEmpty(), - NS_ERROR_FAILURE); - NS_LossyConvertUTF16toASCII tmp(localeString); - tmp.ReplaceChar('-', '_'); - char* locale = (char*)malloc(tmp.Length() + 1); - if (!locale) { - return NS_ERROR_OUT_OF_MEMORY; - } - strcpy(locale, tmp.get()); - - *aICULocale = locale; - - return NS_OK; -} - nsresult nsCollationMacUC::EnsureCollator(const int32_t newStrength) { NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); if (mHasCollator && (mLastStrength == newStrength)) return NS_OK; nsresult res; res = CleanUpCollator(); NS_ENSURE_SUCCESS(res, res); - NS_ENSURE_TRUE(mLocaleICU, NS_ERROR_NOT_INITIALIZED); - UErrorCode status; status = U_ZERO_ERROR; - mCollatorICU = ucol_open(mLocaleICU, &status); + mCollatorICU = ucol_open(mLocale.get(), &status); NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); UCollationStrength strength; UColAttributeValue caseLevel; res = ConvertStrength(newStrength, &strength, &caseLevel); NS_ENSURE_SUCCESS(res, res); status = U_ZERO_ERROR; @@ -137,32 +107,22 @@ nsresult nsCollationMacUC::CleanUpCollat if (mHasCollator) { ucol_close(mCollatorICU); mHasCollator = false; } return NS_OK; } -NS_IMETHODIMP nsCollationMacUC::Initialize(nsILocale* locale) +NS_IMETHODIMP nsCollationMacUC::Initialize(const nsACString& locale) { NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED); nsCOMPtr<nsILocale> appLocale; - nsresult rv; - if (!locale) { - nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); - NS_ENSURE_SUCCESS(rv, rv); - locale = appLocale; - } - - rv = ConvertLocaleICU(locale, &mLocaleICU); - NS_ENSURE_SUCCESS(rv, rv); + mLocale = locale; mInit = true; return NS_OK; } NS_IMETHODIMP nsCollationMacUC::AllocateRawSortKey(int32_t strength, const nsAString& stringIn, uint8_t** key, uint32_t* outLen) {
--- a/intl/locale/mac/nsCollationMacUC.h +++ b/intl/locale/mac/nsCollationMacUC.h @@ -1,19 +1,20 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsCollationMacUC_h_ #define nsCollationMacUC_h_ +#include "mozilla/Attributes.h" #include "nsICollation.h" #include "nsCollation.h" -#include "mozilla/Attributes.h" +#include "nsString.h" #include "unicode/ucol.h" class nsCollationMacUC final : public nsICollation { public: nsCollationMacUC(); @@ -21,24 +22,23 @@ public: NS_DECL_ISUPPORTS // nsICollation interface NS_DECL_NSICOLLATION protected: ~nsCollationMacUC(); - nsresult ConvertLocaleICU(nsILocale* aNSLocale, char** aICULocale); nsresult ConvertStrength(const int32_t aStrength, UCollationStrength* aStrengthOut, UColAttributeValue* aCaseLevelOut); nsresult EnsureCollator(const int32_t newStrength); nsresult CleanUpCollator(void); private: bool mInit; bool mHasCollator; - char* mLocaleICU; + nsCString mLocale; int32_t mLastStrength; UCollator* mCollatorICU; }; #endif /* nsCollationMacUC_h_ */
--- a/intl/locale/nsCollation.cpp +++ b/intl/locale/nsCollation.cpp @@ -5,38 +5,49 @@ #include "nsCollation.h" #include "nsCollationCID.h" #include "nsUnicharUtils.h" #include "prmem.h" #include "nsIUnicodeEncoder.h" #include "nsServiceManagerUtils.h" #include "mozilla/dom/EncodingUtils.h" +#include "mozilla/intl/LocaleService.h" using mozilla::dom::EncodingUtils; //////////////////////////////////////////////////////////////////////////////// NS_DEFINE_CID(kCollationCID, NS_COLLATION_CID); NS_IMPL_ISUPPORTS(nsCollationFactory, nsICollationFactory) -nsresult nsCollationFactory::CreateCollation(nsILocale* locale, nsICollation** instancePtr) +nsresult nsCollationFactory::CreateCollation(nsICollation** instancePtr) +{ + nsAutoCString appLocale; + mozilla::intl::LocaleService::GetInstance()->GetAppLocale(appLocale); + + return CreateCollationForLocale(appLocale, instancePtr); +} + +nsresult +nsCollationFactory::CreateCollationForLocale(const nsACString& locale, nsICollation** instancePtr) { // Create a collation interface instance. // nsICollation *inst; nsresult res; - + res = CallCreateInstance(kCollationCID, &inst); if (NS_FAILED(res)) { return res; } inst->Initialize(locale); + *instancePtr = inst; return res; } //////////////////////////////////////////////////////////////////////////////// nsCollation::nsCollation()
--- a/intl/locale/nsCollation.h +++ b/intl/locale/nsCollation.h @@ -9,26 +9,27 @@ #include "nsICollation.h" #include "nsCOMPtr.h" #include "mozilla/Attributes.h" class nsIUnicodeEncoder; -// Create a collation interface for an input locale. +// Create a collation interface for the current app's locale. // class nsCollationFactory final : public nsICollationFactory { ~nsCollationFactory() {} public: NS_DECL_ISUPPORTS - NS_IMETHOD CreateCollation(nsILocale* locale, nsICollation** instancePtr) override; + NS_IMETHOD CreateCollation(nsICollation** instancePtr) override; + NS_IMETHOD CreateCollationForLocale(const nsACString& locale, nsICollation** instancePtr) override; nsCollationFactory() {} }; struct nsCollation { public:
--- a/intl/locale/nsICollation.idl +++ b/intl/locale/nsICollation.idl @@ -6,27 +6,28 @@ #include "nsILocale.idl" interface nsICollation; [scriptable, uuid(04971e14-d6b3-4ada-8cbb-c3a13842b349)] interface nsICollationFactory : nsISupports { /** - * Create the collation for a given locale. - * - * Use NULL as the locale parameter to use the user's locale preference - * from the operating system. + * Create a new collation for the current application locale. * - * @param locale - * The locale for which to create the collation or null to use - * user preference. - * @return A collation for the given locale. + * @return A new collation. */ - nsICollation CreateCollation(in nsILocale locale); + nsICollation CreateCollation(); + + /** + * Create a new collation for a given locale. + * + * @return A new collation. + */ + nsICollation CreateCollationForLocale(in ACString locale); }; [scriptable, uuid(b0132cc0-3786-4557-9874-910d7def5f93)] interface nsICollation : nsISupports { // use the primary comparison for the given locale - no flags const long kCollationStrengthDefault = 0; @@ -38,17 +39,17 @@ interface nsICollation : nsISupports { // case sensitive collation (default) const long kCollationCaseSensitive = kCollationStrengthDefault; // case insensitive collation const long kCollationCaseInSensitive = (kCollationCaseInsensitiveAscii | kCollationAccentInsenstive); // init this interface to a specified locale (should only be called by collation factory) - void initialize(in nsILocale locale); + void initialize(in ACString locale); // compare two strings // result is same as strcmp long compareString(in long strength, in AString string1, in AString string2); // allocate sort key from input string // returns newly allocated key, and its band its byte length [noscript] void allocateRawSortKey(in long strength,
--- a/intl/locale/tests/unit/test_collation_mac_icu.js +++ b/intl/locale/tests/unit/test_collation_mac_icu.js @@ -15,21 +15,19 @@ function run_test() "¡viva España!", "Österreich", "ä¸å›½", "日本", "한êµ", ]; function test(locale, expected) { - var localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"]. - getService(Ci.nsILocaleService); var collator = Cc["@mozilla.org/intl/collation-factory;1"]. createInstance(Ci.nsICollationFactory). - CreateCollation(localeSvc.newLocale(locale)); + CreateCollationForLocale(locale); var strength = Ci.nsICollation.kCollationStrengthDefault; var actual = input.sort((x, y) => collator.compareString(strength, x,y)); deepEqual(actual, expected, locale); } // Locale en-US; default options. test("en-US", [ "¡viva España!",
--- a/intl/locale/unix/nsCollationUnix.cpp +++ b/intl/locale/unix/nsCollationUnix.cpp @@ -3,17 +3,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <locale.h> #include "prmem.h" #include "nsCollationUnix.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" -#include "nsILocaleService.h" #include "nsIPlatformCharset.h" #include "nsPosixLocale.h" #include "nsCOMPtr.h" #include "nsUnicharUtils.h" #include "nsCRT.h" //#define DEBUG_UNIX_COLLATION inline void nsCollationUnix::DoSetLocale() @@ -39,65 +38,31 @@ nsCollationUnix::nsCollationUnix() : mCo nsCollationUnix::~nsCollationUnix() { if (mCollation) delete mCollation; } NS_IMPL_ISUPPORTS(nsCollationUnix, nsICollation) -nsresult nsCollationUnix::Initialize(nsILocale* locale) +nsresult nsCollationUnix::Initialize(const nsACString& locale) { #define kPlatformLocaleLength 64 NS_ASSERTION(!mCollation, "Should only be initialized once"); nsresult res; mCollation = new nsCollation; - // default platform locale - mLocale.Assign('C'); - - nsAutoString localeStr; - NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_COLLATE##PLATFORM"); - - // get locale string, use app default if no locale specified - if (locale == nullptr) { - nsCOMPtr<nsILocaleService> localeService = - do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); + nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res); + if (NS_SUCCEEDED(res)) { + nsAutoCString mappedCharset; + res = platformCharset->GetDefaultCharsetForLocale(NS_ConvertUTF8toUTF16(locale), mappedCharset); if (NS_SUCCEEDED(res)) { - nsCOMPtr<nsILocale> appLocale; - res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); - if (NS_SUCCEEDED(res)) { - res = appLocale->GetCategory(aCategory, localeStr); - NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info"); - } - } - } - else { - res = locale->GetCategory(aCategory, localeStr); - NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info"); - } - - // Get platform locale and charset name from locale, if available - if (NS_SUCCEEDED(res)) { - // keep the same behavior as 4.x as well as avoiding Linux collation key problem - if (localeStr.LowerCaseEqualsLiteral("en_us")) { // note: locale is in platform format - localeStr.Assign('C'); - } - - nsPosixLocale::GetPlatformLocale(localeStr, mLocale); - - nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res); - if (NS_SUCCEEDED(res)) { - nsAutoCString mappedCharset; - res = platformCharset->GetDefaultCharsetForLocale(localeStr, mappedCharset); - if (NS_SUCCEEDED(res)) { - mCollation->SetCharset(mappedCharset.get()); - } + mCollation->SetCharset(mappedCharset.get()); } } return NS_OK; } nsresult nsCollationUnix::CompareString(int32_t strength,
--- a/intl/locale/windows/nsCollationWin.cpp +++ b/intl/locale/windows/nsCollationWin.cpp @@ -2,17 +2,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/. */ #include "nsCollationWin.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" -#include "nsILocaleService.h" #include "nsIPlatformCharset.h" #include "nsWin32Locale.h" #include "nsCOMPtr.h" #include "prmem.h" #include "plstr.h" #include <windows.h> #undef CompareString @@ -25,59 +24,41 @@ nsCollationWin::nsCollationWin() : mColl } nsCollationWin::~nsCollationWin() { if (mCollation) delete mCollation; } -nsresult nsCollationWin::Initialize(nsILocale* locale) +nsresult nsCollationWin::Initialize(const nsACString& locale) { NS_ASSERTION(!mCollation, "Should only be initialized once."); nsresult res; mCollation = new nsCollation; + NS_ConvertASCIItoUTF16 wideLocale(locale); + // default LCID (en-US) mLCID = 1033; - nsAutoString localeStr; - - // get locale string, use app default if no locale specified - if (!locale) { - nsCOMPtr<nsILocaleService> localeService = - do_GetService(NS_LOCALESERVICE_CONTRACTID); - if (localeService) { - nsCOMPtr<nsILocale> appLocale; - res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); - if (NS_SUCCEEDED(res)) { - res = appLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_COLLATE"), - localeStr); - } - } - } - else { - res = locale->GetCategory(NS_LITERAL_STRING("NSILOCALE_COLLATE"), - localeStr); - } - // Get LCID and charset name from locale, if available LCID lcid; - res = nsWin32Locale::GetPlatformLocale(localeStr, &lcid); + res = nsWin32Locale::GetPlatformLocale(wideLocale, &lcid); if (NS_SUCCEEDED(res)) { mLCID = lcid; } nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID); if (platformCharset) { nsAutoCString mappedCharset; - res = platformCharset->GetDefaultCharsetForLocale(localeStr, mappedCharset); + res = platformCharset->GetDefaultCharsetForLocale(wideLocale, mappedCharset); if (NS_SUCCEEDED(res)) { mCollation->SetCharset(mappedCharset.get()); } } return NS_OK; }
--- a/js/xpconnect/src/XPCLocale.cpp +++ b/js/xpconnect/src/XPCLocale.cpp @@ -121,31 +121,21 @@ private: } bool Compare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval) { nsresult rv; if (!mCollation) { - nsCOMPtr<nsILocaleService> localeService = - do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); + nsCOMPtr<nsICollationFactory> colFactory = + do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { - nsCOMPtr<nsILocale> locale; - rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); - - if (NS_SUCCEEDED(rv)) { - nsCOMPtr<nsICollationFactory> colFactory = - do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); - - if (NS_SUCCEEDED(rv)) { - rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation)); - } - } + rv = colFactory->CreateCollation(getter_AddRefs(mCollation)); } if (NS_FAILED(rv)) { xpc::Throw(cx, rv); return false; } }
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3389,20 +3389,22 @@ nsLayoutUtils::ExpireDisplayPortOnAsyncS break; } nsIScrollableFrame* scrollAncestor = GetAsyncScrollableAncestorFrame(frame); if (!scrollAncestor) { break; } frame = do_QueryFrame(scrollAncestor); MOZ_ASSERT(frame); + if (!frame) { + break; + } MOZ_ASSERT(scrollAncestor->WantAsyncScroll() || frame->PresContext()->PresShell()->GetRootScrollFrame() == frame); - if (nsLayoutUtils::AsyncPanZoomEnabled(frame) && - nsLayoutUtils::HasDisplayPort(frame->GetContent())) { + if (nsLayoutUtils::HasDisplayPort(frame->GetContent())) { scrollAncestor->TriggerDisplayPortExpiration(); // Stop after the first trigger. If it failed, there's no point in // continuing because all the rest of the frames we encounter are going // to be ancestors of |scrollAncestor| which will keep its displayport. // If the trigger succeeded, we stop because when the trigger executes // it will call this function again to trigger the next ancestor up the // chain. break;
--- a/layout/style/ServoCSSRuleList.cpp +++ b/layout/style/ServoCSSRuleList.cpp @@ -128,16 +128,20 @@ ServoCSSRuleList::InsertRule(const nsASt return rv; } nsresult ServoCSSRuleList::DeleteRule(uint32_t aIndex) { nsresult rv = Servo_CssRules_DeleteRule(mRawRules, aIndex); if (!NS_FAILED(rv)) { + uintptr_t rule = mRules[aIndex]; + if (rule > kMaxRuleType) { + CastToPtr(rule)->Release(); + } mRules.RemoveElementAt(aIndex); } return rv; } ServoCSSRuleList::~ServoCSSRuleList() { EnumerateInstantiatedRules([](css::Rule* rule) { rule->Release(); });
--- a/media/libvpx/moz.build +++ b/media/libvpx/moz.build @@ -34,17 +34,20 @@ elif CONFIG['CPU_ARCH'] == 'x86': elif CONFIG['OS_TARGET'] == 'Darwin': ASFLAGS += [ '-I%s/media/libvpx/config/mac/ia32/' % TOPSRCDIR ] CFLAGS += [ '-I%s/media/libvpx/config/mac/ia32/' % TOPSRCDIR ] else: # Android, Linux, BSDs, etc. ASFLAGS += [ '-I%s/media/libvpx/config/linux/ia32/' % TOPSRCDIR ] CFLAGS += [ '-I%s/media/libvpx/config/linux/ia32/' % TOPSRCDIR ] elif CONFIG['CPU_ARCH'] == 'arm': EXPORTS.vpx += files['ARM_EXPORTS'] - ASFLAGS += [ '-I%s/media/libvpx/config/linux/arm/' % TOPSRCDIR ] + ASFLAGS += [ + '-I%s/media/libvpx/config/linux/arm/' % TOPSRCDIR, + '-I%s/libvpx' % OBJDIR, + ] CFLAGS += [ '-I%s/media/libvpx/config/linux/arm/' % TOPSRCDIR ] arm_asm_files = files['ARM_SOURCES'] if CONFIG['VPX_AS_CONVERSION']: SOURCES += sorted([ "!%s.S" % f if f.endswith('.asm') else f for f in arm_asm_files ]) @@ -83,18 +86,16 @@ if CONFIG['OS_TARGET'] == 'Android': # the OS they're on, so do it for them. DEFINES['__linux__'] = True if not CONFIG['MOZ_WEBRTC']: SOURCES += [ '%%%s/sources/android/cpufeatures/cpu-features.c' % CONFIG['ANDROID_NDK'], ] - ASFLAGS += ['-I%s/libvpx' % OBJDIR] - if CONFIG['CLANG_CL'] or not CONFIG['_MSC_VER']: for f in SOURCES: if f.endswith('.c'): if 'sse2.c' in f: SOURCES[f].flags += CONFIG['SSE2_FLAGS'] if 'ssse3.c' in f: SOURCES[f].flags += ['-mssse3'] if 'sse4.c' in f:
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -662,17 +662,16 @@ public class BrowserApp extends GeckoApp // shut down (in which case trying to perform UI changes, such as showing // fragments below, will crash). return; } final TabHistoryFragment fragment = TabHistoryFragment.newInstance(historyPageList, toIndex); final FragmentManager fragmentManager = getSupportFragmentManager(); GeckoAppShell.vibrateOnHapticFeedbackEnabled(getResources().getIntArray(R.array.long_press_vibrate_msec)); - if (BrowserApp.this.isForegrounded()) fragment.show(R.id.tab_history_panel, fragmentManager.beginTransaction(), TAB_HISTORY_FRAGMENT_TAG); } }); } }); mBrowserToolbar.setTabHistoryController(tabHistoryController); final String action = intent.getAction();
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java +++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java @@ -1,23 +1,30 @@ /* -*- 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.customtabs; +import android.app.PendingIntent; +import android.content.Intent; +import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.support.annotation.VisibleForTesting; +import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.util.Log; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.TextView; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.GeckoApp; @@ -30,17 +37,16 @@ import java.lang.reflect.Field; import static android.support.customtabs.CustomTabsIntent.EXTRA_TOOLBAR_COLOR; public class CustomTabsActivity extends GeckoApp implements Tabs.OnTabsChangedListener { private static final String LOGTAG = "CustomTabsActivity"; private static final String SAVED_TOOLBAR_COLOR = "SavedToolbarColor"; private static final String SAVED_TOOLBAR_TITLE = "SavedToolbarTitle"; private static final int NO_COLOR = -1; - private Toolbar toolbar; private ActionBar actionBar; private int tabId = -1; private boolean useDomainTitle = true; private int toolbarColor; private String toolbarTitle; @@ -194,25 +200,59 @@ public class CustomTabsActivity extends final Tab tab = tabs.getTab(lastSelectedTabId); if (tab == null) { finish(); } } super.onResume(); } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + insertActionButton(menu, getIntent()); + return super.onPrepareOptionsMenu(menu); + } + public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; + case R.id.action_button: + onActionButtonClicked(); + return true; } return super.onOptionsItemSelected(item); } + /** + * To insert a MenuItem (as an ActionButton) into Menu. + * + * @param menu The options menu in which to place items. + * @param intent which to launch this activity + * @return the MenuItem which be created and inserted into menu. Otherwise, null. + */ + @VisibleForTesting + MenuItem insertActionButton(Menu menu, Intent intent) { + if (!IntentUtil.hasActionButton(intent)) { + return null; + } + + // TODO: Bug 1336373 - Action button icon should support tint + MenuItem item = menu.add(Menu.NONE, + R.id.action_button, + Menu.NONE, + IntentUtil.getActionButtonDescription(intent)); + Bitmap bitmap = IntentUtil.getActionButtonIcon(intent); + item.setIcon(new BitmapDrawable(getResources(), bitmap)); + MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_ALWAYS); + + return item; + } + private void updateActionBarWithToolbar(final Toolbar toolbar) { setSupportActionBar(toolbar); final ActionBar ab = getSupportActionBar(); if (ab != null) { ab.setDisplayHomeAsUpEnabled(true); } } @@ -224,9 +264,18 @@ public class CustomTabsActivity extends toolbar.setBackgroundColor(toolbarColor); final Window window = getWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(ColorUtil.darken(toolbarColor, 0.25)); } } + + private void onActionButtonClicked() { + PendingIntent pendingIntent = IntentUtil.getActionButtonPendingIntent(getIntent()); + try { + pendingIntent.send(); + } catch (PendingIntent.CanceledException e) { + Log.w(LOGTAG, "Action Button clicked, but pending intent was canceled", e); + } + } }
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/IntentUtil.java +++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/IntentUtil.java @@ -1,16 +1,18 @@ /* -*- 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.customtabs; +import android.app.PendingIntent; import android.content.Intent; +import android.graphics.Bitmap; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.customtabs.CustomTabsIntent; /** * A utility class for CustomTabsActivity to extract information from intent. * For example, this class helps to extract exit-animation resource id. @@ -23,16 +25,85 @@ class IntentUtil { private static final String PREFIX = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? "android:activity." : "android:"; private static final String KEY_PACKAGE_NAME = PREFIX + "packageName"; private static final String KEY_ANIM_ENTER_RES_ID = PREFIX + "animEnterRes"; private static final String KEY_ANIM_EXIT_RES_ID = PREFIX + "animExitRes"; /** + * To determine whether the intent has necessary information to build an Action-Button. + * + * @param intent which to launch a Custom-Tabs-Activity + * @return true, if intent has all necessary information. + */ + static boolean hasActionButton(@NonNull Intent intent) { + return (getActionButtonBundle(intent) != null) + && (getActionButtonIcon(intent) != null) + && (getActionButtonDescription(intent) != null) + && (getActionButtonPendingIntent(intent) != null); + } + + /** + * To extract bitmap icon from intent for Action-Button. + * + * @param intent which to launch a Custom-Tabs-Activity + * @return bitmap icon, if any. Otherwise, null. + */ + static Bitmap getActionButtonIcon(@NonNull Intent intent) { + final Bundle bundle = getActionButtonBundle(intent); + return (bundle == null) ? null : (Bitmap) bundle.getParcelable(CustomTabsIntent.KEY_ICON); + } + + /** + * To extract description from intent for Action-Button. This description is used for + * accessibility. + * + * @param intent which to launch a Custom-Tabs-Activity + * @return description, if any. Otherwise, null. + */ + static String getActionButtonDescription(@NonNull Intent intent) { + final Bundle bundle = getActionButtonBundle(intent); + return (bundle == null) ? null : bundle.getString(CustomTabsIntent.KEY_DESCRIPTION); + } + + /** + * To extract pending-intent from intent for Action-Button. + * + * @param intent which to launch a Custom-Tabs-Activity + * @return PendingIntent, if any. Otherwise, null. + */ + static PendingIntent getActionButtonPendingIntent(@NonNull Intent intent) { + final Bundle bundle = getActionButtonBundle(intent); + return (bundle == null) + ? null + : (PendingIntent) bundle.getParcelable(CustomTabsIntent.KEY_PENDING_INTENT); + } + + /** + * To know whether the Action-Button should be tinted. + * + * @param intent which to launch a Custom-Tabs-Activity + * @return true, if Action-Button should be tinted. Default value is false. + */ + static boolean isActionButtonTinted(@NonNull Intent intent) { + return intent.getBooleanExtra(CustomTabsIntent.EXTRA_TINT_ACTION_BUTTON, false); + } + + /** + * To extract extra Action-button bundle from an intent. + * + * @param intent which to launch a Custom-Tabs-Activity + * @return bundle for Action-Button, if any. Otherwise, null. + */ + private static Bundle getActionButtonBundle(@NonNull Intent intent) { + return intent.getBundleExtra(CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE); + } + + /** * To get package name of 3rd-party-app from an intent. * If the app defined extra exit-animation to use, it should also provide its package name * to get correct animation resource. * * @param intent which to launch a Custom-Tabs-Activity * @return package name, if the intent defined extra exit-animation bundle. Otherwise, null. */ static String getAnimationPackageName(@NonNull Intent intent) {
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java @@ -2073,17 +2073,17 @@ public final class BrowserDatabaseHelper case 25: upgradeDatabaseFrom24to25(db); break; case 26: upgradeDatabaseFrom25to26(db); break; - // case 27 occurs in UrlMetadataTable.onUpgrade + // case 27 occurs in URLImageDataTable.onUpgrade case 28: upgradeDatabaseFrom27to28(db); break; case 29: upgradeDatabaseFrom28to29(db); break;
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java @@ -159,18 +159,18 @@ public class BrowserProvider extends Sha static final Map<String, String> THUMBNAILS_PROJECTION_MAP; static final Map<String, String> URL_ANNOTATIONS_PROJECTION_MAP; static final Map<String, String> VISIT_PROJECTION_MAP; static final Map<String, String> PAGE_METADATA_PROJECTION_MAP; static final Table[] sTables; static { sTables = new Table[] { - // See awful shortcut assumption hack in getURLMetadataTable. - new URLMetadataTable() + // See awful shortcut assumption hack in getURLImageDataTable. + new URLImageDataTable() }; // We will reuse this. HashMap<String, String> map; // Bookmarks URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks", BOOKMARKS); URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks/#", BOOKMARKS_ID); URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks/parents", BOOKMARKS_PARENT); @@ -365,18 +365,18 @@ public class BrowserProvider extends Sha public void shutdown() { LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mShrinkMemoryReceiver); super.shutdown(); } // Convenience accessor. // Assumes structure of sTables! - private URLMetadataTable getURLMetadataTable() { - return (URLMetadataTable) sTables[0]; + private URLImageDataTable getURLImageDataTable() { + return (URLImageDataTable) sTables[0]; } private static boolean hasFaviconsInProjection(String[] projection) { if (projection == null) return true; for (int i = 0; i < projection.length; ++i) { if (projection[i].equals(FaviconColumns.FAVICON) || projection[i].equals(FaviconColumns.FAVICON_URL)) return true; @@ -2199,17 +2199,17 @@ public class BrowserProvider extends Sha + " AND " + History.URL + " IS NOT NULL" + " UNION ALL SELECT " + Bookmarks.URL + " FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.IS_DELETED + " = 0" + " AND " + Bookmarks.URL + " IS NOT NULL)"; return deleteFavicons(uri, faviconSelection, null) + deleteThumbnails(uri, thumbnailSelection, null) + - getURLMetadataTable().deleteUnused(getWritableDatabase(uri)); + getURLImageDataTable().deleteUnused(getWritableDatabase(uri)); } @Override public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { final int numOperations = operations.size(); final ContentProviderResult[] results = new ContentProviderResult[numOperations];
--- a/mobile/android/base/java/org/mozilla/gecko/db/LocalURLMetadata.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalURLMetadata.java @@ -29,27 +29,27 @@ import android.util.Log; import android.util.LruCache; // Holds metadata info about URLs. Supports some helper functions for getting back a HashMap of key value data. public class LocalURLMetadata implements URLMetadata { private static final String LOGTAG = "GeckoURLMetadata"; private final Uri uriWithProfile; public LocalURLMetadata(String mProfile) { - uriWithProfile = DBUtils.appendProfileWithDefault(mProfile, URLMetadataTable.CONTENT_URI); + uriWithProfile = DBUtils.appendProfileWithDefault(mProfile, URLImageDataTable.CONTENT_URI); } // A list of columns in the table. It's used to simplify some loops for reading/writing data. private static final Set<String> COLUMNS; static { final HashSet<String> tempModel = new HashSet<>(4); - tempModel.add(URLMetadataTable.URL_COLUMN); - tempModel.add(URLMetadataTable.TILE_IMAGE_URL_COLUMN); - tempModel.add(URLMetadataTable.TILE_COLOR_COLUMN); - tempModel.add(URLMetadataTable.TOUCH_ICON_COLUMN); + tempModel.add(URLImageDataTable.URL_COLUMN); + tempModel.add(URLImageDataTable.TILE_IMAGE_URL_COLUMN); + tempModel.add(URLImageDataTable.TILE_COLOR_COLUMN); + tempModel.add(URLImageDataTable.TOUCH_ICON_COLUMN); COLUMNS = Collections.unmodifiableSet(tempModel); } // Store a cache of recent results. This number is chosen to match the max number of tiles on about:home private static final int CACHE_SIZE = 9; // Note: Members of this cache are unmodifiable. private final LruCache<String, Map<String, Object>> cache = new LruCache<String, Map<String, Object>>(CACHE_SIZE); @@ -83,17 +83,17 @@ public class LocalURLMetadata implements ArrayList<Integer> sizes = new ArrayList<Integer>(icons.length()); while (keys.hasNext()) { sizes.add(new Integer(keys.next())); } final int bestSize = LoadFaviconResult.selectBestSizeFromList(sizes, preferredSize); final String iconURL = icons.getString(Integer.toString(bestSize)); - data.put(URLMetadataTable.TOUCH_ICON_COLUMN, iconURL); + data.put(URLImageDataTable.TOUCH_ICON_COLUMN, iconURL); } } catch (JSONException e) { Log.w(LOGTAG, "Exception processing touchIconList for LocalURLMetadata; ignoring.", e); } return Collections.unmodifiableMap(data); } @@ -164,39 +164,39 @@ public class LocalURLMetadata implements } } // If everything was in the cache, we're done! if (urlsToQuery.size() == 0) { return Collections.unmodifiableMap(data); } - final String selection = DBUtils.computeSQLInClause(urlsToQuery.size(), URLMetadataTable.URL_COLUMN); + final String selection = DBUtils.computeSQLInClause(urlsToQuery.size(), URLImageDataTable.URL_COLUMN); List<String> columns = requestedColumns; // We need the url to build our final HashMap, so we force it to be included in the query. - if (!columns.contains(URLMetadataTable.URL_COLUMN)) { + if (!columns.contains(URLImageDataTable.URL_COLUMN)) { // The requestedColumns may be immutable (e.g. if the caller used Collections.singletonList), hence // we have to create a copy. columns = new ArrayList<String>(columns); - columns.add(URLMetadataTable.URL_COLUMN); + columns.add(URLImageDataTable.URL_COLUMN); } final Cursor cursor = cr.query(uriWithProfile, columns.toArray(new String[columns.size()]), // columns, selection, // selection urlsToQuery.toArray(new String[urlsToQuery.size()]), // selectionargs null); try { if (!cursor.moveToFirst()) { return Collections.unmodifiableMap(data); } do { final Map<String, Object> metadata = fromCursor(cursor); - final String url = cursor.getString(cursor.getColumnIndexOrThrow(URLMetadataTable.URL_COLUMN)); + final String url = cursor.getString(cursor.getColumnIndexOrThrow(URLImageDataTable.URL_COLUMN)); data.put(url, metadata); cache.put(url, metadata); } while (cursor.moveToNext()); } finally { cursor.close(); } @@ -225,16 +225,16 @@ public class LocalURLMetadata implements if (values.size() == 0) { return; } Uri uri = uriWithProfile.buildUpon() .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true") .build(); - cr.update(uri, values, URLMetadataTable.URL_COLUMN + "=?", new String[] { - (String) data.get(URLMetadataTable.URL_COLUMN) + cr.update(uri, values, URLImageDataTable.URL_COLUMN + "=?", new String[] { + (String) data.get(URLImageDataTable.URL_COLUMN) }); } catch (Exception ex) { Log.e(LOGTAG, "error saving", ex); } } }
rename from mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java rename to mobile/android/base/java/org/mozilla/gecko/db/URLImageDataTable.java --- a/mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/URLImageDataTable.java @@ -8,33 +8,33 @@ package org.mozilla.gecko.db; import org.mozilla.gecko.db.BrowserContract.Bookmarks; import org.mozilla.gecko.db.BrowserContract.History; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; // Holds metadata info about urls. Supports some helper functions for getting back a HashMap of key value data. -public class URLMetadataTable extends BaseTable { - private static final String LOGTAG = "GeckoURLMetadataTable"; +public class URLImageDataTable extends BaseTable { + private static final String LOGTAG = "GeckoURLImageDataTable"; private static final String TABLE = "metadata"; // Name of the table in the db private static final int TABLE_ID_NUMBER = BrowserProvider.METADATA; // Uri for querying this table public static final Uri CONTENT_URI = Uri.withAppendedPath(BrowserContract.AUTHORITY_URI, "metadata"); // Columns in the table public static final String ID_COLUMN = "id"; public static final String URL_COLUMN = "url"; public static final String TILE_IMAGE_URL_COLUMN = "tileImage"; public static final String TILE_COLOR_COLUMN = "tileColor"; public static final String TOUCH_ICON_COLUMN = "touchIcon"; - URLMetadataTable() { } + URLImageDataTable() { } @Override protected String getTable() { return TABLE; } @Override public void onCreate(SQLiteDatabase db) {
--- a/mobile/android/base/java/org/mozilla/gecko/home/TopSitesPanel.java +++ b/mobile/android/base/java/org/mozilla/gecko/home/TopSitesPanel.java @@ -1,17 +1,17 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.home; -import static org.mozilla.gecko.db.URLMetadataTable.TILE_COLOR_COLUMN; -import static org.mozilla.gecko.db.URLMetadataTable.TILE_IMAGE_URL_COLUMN; +import static org.mozilla.gecko.db.URLImageDataTable.TILE_COLOR_COLUMN; +import static org.mozilla.gecko.db.URLImageDataTable.TILE_IMAGE_URL_COLUMN; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Future;
--- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -377,18 +377,18 @@ gbjar.sources += ['java/org/mozilla/geck 'db/SearchHistoryProvider.java', 'db/SharedBrowserDatabaseProvider.java', 'db/SQLiteBridgeContentProvider.java', 'db/SuggestedSites.java', 'db/Table.java', 'db/TabsAccessor.java', 'db/TabsProvider.java', 'db/UrlAnnotations.java', + 'db/URLImageDataTable.java', 'db/URLMetadata.java', - 'db/URLMetadataTable.java', 'delegates/BookmarkStateChangeDelegate.java', 'delegates/BrowserAppDelegate.java', 'delegates/BrowserAppDelegateWithReference.java', 'delegates/OfflineTabStatusDelegate.java', 'delegates/ScreenshotDelegate.java', 'delegates/TabsTrayVisibilityAwareDelegate.java', 'DevToolsAuthHelper.java', 'distribution/Distribution.java',
--- a/mobile/android/base/resources/values/ids.xml +++ b/mobile/android/base/resources/values/ids.xml @@ -14,10 +14,11 @@ <item type="id" name="recycler_view_click_support" /> <item type="id" name="range_list"/> <item type="id" name="pref_header_general"/> <item type="id" name="pref_header_privacy"/> <item type="id" name="pref_header_search"/> <item type="id" name="updateServicePermissionNotification" /> <item type="id" name="websiteContentNotification" /> <item type="id" name="foregroundNotification" /> + <item type="id" name="action_button"/> </resources>
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java @@ -1,15 +1,16 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.mozilla.gecko.AppConstants; import org.mozilla.gecko.SysInfo; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.Build; import android.util.Log; @@ -86,16 +87,22 @@ public final class HardwareUtils { public static boolean isX86System() { return Build.CPU_ABI != null && Build.CPU_ABI.equals("x86"); } /** * @return false if the current system is not supported (e.g. APK/system ABI mismatch). */ public static boolean isSupportedSystem() { + // We've had crash reports from users on API 10 (with minSDK==15). That shouldn't even install, + // but since it does we need to protect against it: + if (Build.VERSION.SDK_INT < AppConstants.Versions.MIN_SDK_VERSION) { + return false; + } + // See http://developer.android.com/ndk/guides/abis.html final boolean isSystemARM = isARMSystem(); final boolean isSystemX86 = isX86System(); boolean isAppARM = BuildConfig.ANDROID_CPU_ARCH.startsWith("armeabi-v7a"); boolean isAppX86 = BuildConfig.ANDROID_CPU_ARCH.startsWith("x86"); // Only reject known incompatible ABIs. Better safe than sorry.
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestCustomTabsActivity.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestCustomTabsActivity.java @@ -1,26 +1,34 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.gecko.customtabs; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.support.annotation.AnimRes; import android.support.customtabs.CustomTabsIntent; +import android.view.Menu; +import android.view.MenuItem; import junit.framework.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.internal.util.reflection.Whitebox; +import org.mozilla.gecko.R; import org.mozilla.gecko.background.testhelpers.TestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.fakes.RoboMenu; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -37,16 +45,17 @@ public class TestCustomTabsActivity { private final int exitRes = 0x456; // arbitrary number as animation resource id @Before public void setUp() { spyContext = spy(RuntimeEnvironment.application); doReturn(THIRD_PARTY_PACKAGE_NAME).when(spyContext).getPackageName(); spyActivity = spy(new CustomTabsActivity()); + doReturn(RuntimeEnvironment.application.getResources()).when(spyActivity).getResources(); final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); builder.setExitAnimations(spyContext, enterRes, exitRes); final Intent i = builder.build().intent; } /** * Activity should not call overridePendingTransition if custom animation does not exist. @@ -86,9 +95,36 @@ public class TestCustomTabsActivity { builder.setExitAnimations(spyContext, enterRes, exitRes); final Intent i = builder.build().intent; doReturn(i).when(spyActivity).getIntent(); Whitebox.setInternalState(spyActivity, "usingCustomAnimation", true); Assert.assertEquals(THIRD_PARTY_PACKAGE_NAME, spyActivity.getPackageName()); } + + @Test + public void testInsertActionButton() { + // create properties for CustomTabsIntent + final String description = "Description"; + final Intent actionIntent = new Intent(Intent.ACTION_VIEW); + final int reqCode = 0x123; + final PendingIntent pendingIntent = PendingIntent.getActivities(spyContext, + reqCode, + new Intent[]{actionIntent}, + PendingIntent.FLAG_CANCEL_CURRENT); + final Bitmap bitmap = BitmapFactory.decodeResource( + spyContext.getResources(), + R.drawable.ic_action_settings); // arbitrary icon resource + + // To create a CustomTabsIntent which is asking for ActionButton. + final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + builder.setActionButton(bitmap, description, pendingIntent, true); + + // CustomTabsActivity should return a MenuItem with corresponding attributes. + Menu menu = new RoboMenu(spyContext); + MenuItem item = spyActivity.insertActionButton(menu, builder.build().intent); + Assert.assertNotNull(item); + Assert.assertEquals(item.getTitle(), description); + Assert.assertEquals(0, item.getOrder()); // should be the first one + Assert.assertTrue(item.isVisible()); + } } \ No newline at end of file
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestIntentUtil.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/customtabs/TestIntentUtil.java @@ -1,41 +1,87 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.gecko.customtabs; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.support.annotation.AnimRes; import android.support.customtabs.CustomTabsIntent; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mozilla.gecko.R; import org.mozilla.gecko.background.testhelpers.TestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Objects; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @RunWith(TestRunner.class) public class TestIntentUtil { private static final String THIRD_PARTY_PACKAGE_NAME = "mozilla.unit.test"; private Context spyContext; // 3rd party app context @Before public void setUp() { spyContext = spy(RuntimeEnvironment.application); doReturn(THIRD_PARTY_PACKAGE_NAME).when(spyContext).getPackageName(); } @Test + public void testIntentWithActionButton() { + // create properties for CustomTabsIntent + final String description = "Description"; + final boolean tinted = true; + final Intent actionIntent = new Intent(Intent.ACTION_VIEW); + final int reqCode = 0x123; + final PendingIntent pendingIntent = PendingIntent.getActivities(spyContext, + reqCode, + new Intent[]{actionIntent}, + PendingIntent.FLAG_CANCEL_CURRENT); + + final Bitmap bitmap = BitmapFactory.decodeResource( + spyContext.getResources(), + R.drawable.ic_action_settings); // arbitrary icon resource + + final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + builder.setActionButton(bitmap, description, pendingIntent, tinted); + + Intent intent = builder.build().intent; + Assert.assertTrue(IntentUtil.hasActionButton(intent)); + Assert.assertEquals(tinted, IntentUtil.isActionButtonTinted(intent)); + Assert.assertEquals(bitmap, IntentUtil.getActionButtonIcon(intent)); + Assert.assertEquals(description, IntentUtil.getActionButtonDescription(intent)); + Assert.assertTrue( + Objects.equals(pendingIntent, IntentUtil.getActionButtonPendingIntent(intent))); + } + + @Test + public void testIntentWithoutActionButton() { + final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + + Intent intent = builder.build().intent; + Assert.assertFalse(IntentUtil.hasActionButton(intent)); + Assert.assertFalse(IntentUtil.isActionButtonTinted(intent)); + Assert.assertNull(IntentUtil.getActionButtonIcon(intent)); + Assert.assertNull(IntentUtil.getActionButtonDescription(intent)); + Assert.assertNull(IntentUtil.getActionButtonPendingIntent(intent)); + } + + @Test public void testIntentWithCustomAnimation() { @AnimRes final int enterRes = 0x123; // arbitrary number as animation resource id @AnimRes final int exitRes = 0x456; // arbitrary number as animation resource id final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); builder.setExitAnimations(spyContext, enterRes, exitRes); final Intent i = builder.build().intent;
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBrowserProvider.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBrowserProvider.java @@ -11,17 +11,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.db.BrowserContract.UrlAnnotations.SyncStatus; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.URLMetadata; -import org.mozilla.gecko.db.URLMetadataTable; +import org.mozilla.gecko.db.URLImageDataTable; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; @@ -193,20 +193,20 @@ public class testBrowserProvider extends thumbnailEntry.put(BrowserContract.Thumbnails.DATA, data.getBytes("UTF8")); return thumbnailEntry; } private ContentValues createUrlMetadataEntry(final String url, final String tileImage, final String tileColor, final String touchIcon) { final ContentValues values = new ContentValues(); - values.put(URLMetadataTable.URL_COLUMN, url); - values.put(URLMetadataTable.TILE_IMAGE_URL_COLUMN, tileImage); - values.put(URLMetadataTable.TILE_COLOR_COLUMN, tileColor); - values.put(URLMetadataTable.TOUCH_ICON_COLUMN, touchIcon); + values.put(URLImageDataTable.URL_COLUMN, url); + values.put(URLImageDataTable.TILE_IMAGE_URL_COLUMN, tileImage); + values.put(URLImageDataTable.TILE_COLOR_COLUMN, tileColor); + values.put(URLImageDataTable.TOUCH_ICON_COLUMN, touchIcon); return values; } private ContentValues createUrlAnnotationEntry(final String url, final String key, final String value, final long dateCreated) { final ContentValues values = new ContentValues(); values.put(BrowserContract.UrlAnnotations.URL, url); values.put(BrowserContract.UrlAnnotations.KEY, key); @@ -256,18 +256,18 @@ public class testBrowserProvider extends private Cursor getUrlAnnotationByUrl(final String url) throws Exception { return mProvider.query(BrowserContract.UrlAnnotations.CONTENT_URI, null, BrowserContract.UrlAnnotations.URL + " = ?", new String[] { url }, null); } private Cursor getUrlMetadataByUrl(final String url) throws Exception { - return mProvider.query(URLMetadataTable.CONTENT_URI, null, - URLMetadataTable.URL_COLUMN + " = ?", + return mProvider.query(URLImageDataTable.CONTENT_URI, null, + URLImageDataTable.URL_COLUMN + " = ?", new String[] { url }, null); } @Override public void setUp() throws Exception { super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db"); @@ -1530,19 +1530,19 @@ public class testBrowserProvider extends final String url2 = "http://hello.org"; private void testInsertionViaContentProvider() throws Exception { final String tileImage = "http://mozilla.org/tileImage.png"; final String tileColor = "#FF0000"; final String touchIcon = "http://mozilla.org/touchIcon.png"; // We can only use update since the redirection machinery doesn't exist for insert - mProvider.update(URLMetadataTable.CONTENT_URI.buildUpon().appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build(), + mProvider.update(URLImageDataTable.CONTENT_URI.buildUpon().appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build(), createUrlMetadataEntry(url1, tileImage, tileColor, touchIcon), - URLMetadataTable.URL_COLUMN + "=?", + URLImageDataTable.URL_COLUMN + "=?", new String[] {url1} ); final Cursor c = getUrlMetadataByUrl(url1); try { mAsserter.is(c.getCount(), 1, "URL metadata inserted via Content Provider not found"); } finally { c.close(); @@ -1550,20 +1550,20 @@ public class testBrowserProvider extends } private void testInsertionViaUrlMetadata() throws Exception { final String tileImage = "http://hello.org/tileImage.png"; final String tileColor = "#FF0000"; final String touchIcon = "http://hello.org/touchIcon.png"; final Map<String, Object> data = new HashMap<>(); - data.put(URLMetadataTable.URL_COLUMN, url2); - data.put(URLMetadataTable.TILE_IMAGE_URL_COLUMN, tileImage); - data.put(URLMetadataTable.TILE_COLOR_COLUMN, tileColor); - data.put(URLMetadataTable.TOUCH_ICON_COLUMN, touchIcon); + data.put(URLImageDataTable.URL_COLUMN, url2); + data.put(URLImageDataTable.TILE_IMAGE_URL_COLUMN, tileImage); + data.put(URLImageDataTable.TILE_COLOR_COLUMN, tileColor); + data.put(URLImageDataTable.TOUCH_ICON_COLUMN, touchIcon); BrowserDB.from(getTestProfile()).getURLMetadata().save(mResolver, data); final Cursor c = getUrlMetadataByUrl(url2); try { mAsserter.is(c.moveToFirst(), true, "URL metadata inserted via UrlMetadata not found"); } finally { c.close(); @@ -1581,53 +1581,53 @@ public class testBrowserProvider extends URLMetadata metadata = BrowserDB.from(getTestProfile()).getURLMetadata(); Map<String, Map<String, Object>> results; Map<String, Object> urlData; // 1: retrieve just touch Icons for URL 1 results = metadata.getForURLs(mResolver, Collections.singletonList(url1), - Collections.singletonList(URLMetadataTable.TOUCH_ICON_COLUMN)); + Collections.singletonList(URLImageDataTable.TOUCH_ICON_COLUMN)); mAsserter.is(results.containsKey(url1), true, "URL 1 not found in results"); urlData = results.get(url1); - mAsserter.is(urlData.containsKey(URLMetadataTable.TOUCH_ICON_COLUMN), true, "touchIcon column missing in UrlMetadata results"); + mAsserter.is(urlData.containsKey(URLImageDataTable.TOUCH_ICON_COLUMN), true, "touchIcon column missing in UrlMetadata results"); // 2: retrieve just tile color for URL 2 results = metadata.getForURLs(mResolver, Collections.singletonList(url2), - Collections.singletonList(URLMetadataTable.TILE_COLOR_COLUMN)); + Collections.singletonList(URLImageDataTable.TILE_COLOR_COLUMN)); mAsserter.is(results.containsKey(url2), true, "URL 2 not found in results"); urlData = results.get(url2); - mAsserter.is(urlData.containsKey(URLMetadataTable.TILE_COLOR_COLUMN), true, "touchIcon column missing in UrlMetadata results"); + mAsserter.is(urlData.containsKey(URLImageDataTable.TILE_COLOR_COLUMN), true, "touchIcon column missing in UrlMetadata results"); // 3: retrieve all columns for both URLs final List<String> urls = Arrays.asList(url1, url2); results = metadata.getForURLs(mResolver, urls, - Arrays.asList(URLMetadataTable.TILE_IMAGE_URL_COLUMN, - URLMetadataTable.TILE_COLOR_COLUMN, - URLMetadataTable.TOUCH_ICON_COLUMN + Arrays.asList(URLImageDataTable.TILE_IMAGE_URL_COLUMN, + URLImageDataTable.TILE_COLOR_COLUMN, + URLImageDataTable.TOUCH_ICON_COLUMN )); mAsserter.is(results.containsKey(url1), true, "URL 1 not found in results"); mAsserter.is(results.containsKey(url2), true, "URL 2 not found in results"); for (final String url : urls) { urlData = results.get(url); - mAsserter.is(urlData.containsKey(URLMetadataTable.TILE_IMAGE_URL_COLUMN), true, "touchIcon column missing in UrlMetadata results"); - mAsserter.is(urlData.containsKey(URLMetadataTable.TILE_COLOR_COLUMN), true, "touchIcon column missing in UrlMetadata results"); - mAsserter.is(urlData.containsKey(URLMetadataTable.TOUCH_ICON_COLUMN), true, "touchIcon column missing in UrlMetadata results"); + mAsserter.is(urlData.containsKey(URLImageDataTable.TILE_IMAGE_URL_COLUMN), true, "touchIcon column missing in UrlMetadata results"); + mAsserter.is(urlData.containsKey(URLImageDataTable.TILE_COLOR_COLUMN), true, "touchIcon column missing in UrlMetadata results"); + mAsserter.is(urlData.containsKey(URLImageDataTable.TOUCH_ICON_COLUMN), true, "touchIcon column missing in UrlMetadata results"); } } } private class TestCombinedView extends TestCase { @Override public void test() throws Exception { final String TITLE_1 = "Test Page 1";
--- a/moz.configure +++ b/moz.configure @@ -152,16 +152,27 @@ option('--build-backends', nargs='+', de choices=build_backends_choices, help='Build backends to generate') @depends('--build-backends') def build_backends(backends): return backends set_config('BUILD_BACKENDS', build_backends) +# Determine whether to build the gtest xul. This happens in automation +# on Desktop platforms with the exception of Windows PGO, where linking +# xul-gtest.dll takes too long. +@depends('MOZ_PGO', build_project, target, 'MOZ_AUTOMATION', + when='--enable-compile-environment') +def build_gtest(pgo, build_project, target, automation): + if (automation and build_project == 'browser' and + not (pgo and target.os == 'WINNT')): + return True + +set_config('LINK_GTEST_DURING_COMPILE', build_gtest) # Awk detection # ============================================================== awk = check_prog('AWK', ('gawk', 'mawk', 'nawk', 'awk')) # Until the AWK variable is not necessary in old-configure @depends(awk) def awk_for_old_configure(value):
--- a/netwerk/base/nsDirectoryIndexStream.cpp +++ b/netwerk/base/nsDirectoryIndexStream.cpp @@ -17,18 +17,16 @@ #include "nsEscape.h" #include "nsDirectoryIndexStream.h" #include "mozilla/Logging.h" #include "prtime.h" #include "nsISimpleEnumerator.h" #ifdef THREADSAFE_I18N #include "nsCollationCID.h" #include "nsICollation.h" -#include "nsILocale.h" -#include "nsILocaleService.h" #endif #include "nsIFile.h" #include "nsURLHelper.h" #include "nsNativeCharsetUtils.h" // NOTE: This runs on the _file transport_ thread. // The problem is that now that we're actually doing something with the data, // we want to do stuff like i18n sorting. However, none of the collation stuff @@ -115,30 +113,22 @@ nsDirectoryIndexStream::Init(nsIFile* aD if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIFile> file = do_QueryInterface(elem); if (file) mArray.AppendObject(file); // addrefs } } #ifdef THREADSAFE_I18N - nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID, - &rv); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsILocale> locale; - rv = ls->GetApplicationLocale(getter_AddRefs(locale)); - if (NS_FAILED(rv)) return rv; - nsCOMPtr<nsICollationFactory> cf = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsICollation> coll; - rv = cf->CreateCollation(locale, getter_AddRefs(coll)); + rv = cf->CreateCollation(getter_AddRefs(coll)); if (NS_FAILED(rv)) return rv; mArray.Sort(compare, coll); #else mArray.Sort(compare, nullptr); #endif mBuf.AppendLiteral("300: ");
--- a/netwerk/protocol/about/nsAboutCache.cpp +++ b/netwerk/protocol/about/nsAboutCache.cpp @@ -560,17 +560,17 @@ nsAboutCache::Channel::FlushBuffer() } return rv; } NS_IMETHODIMP nsAboutCache::GetURIFlags(nsIURI *aURI, uint32_t *result) { - *result = 0; + *result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT; return NS_OK; } // static nsresult nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) { nsAboutCache* about = new nsAboutCache();
--- a/netwerk/protocol/about/nsAboutCacheEntry.cpp +++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp @@ -107,17 +107,18 @@ nsAboutCacheEntry::NewChannel(nsIURI* ur channel.forget(result); return NS_OK; } NS_IMETHODIMP nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result) { - *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT; + *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT | + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT; return NS_OK; } //----------------------------------------------------------------------------- // nsAboutCacheEntry::Channel nsresult nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo)
--- a/netwerk/test/browser/browser.ini +++ b/netwerk/test/browser/browser.ini @@ -1,10 +1,11 @@ [DEFAULT] support-files = dummy.html +[browser_about_cache.js] [browser_NetUtil.js] [browser_child_resource.js] skip-if = e10s && debug && os == "linux" && bits == 64 [browser_post_file.js] [browser_nsIFormPOSTActionChannel.js] skip-if = e10s # protocol handler and channel does not work in content process
new file mode 100644 --- /dev/null +++ b/netwerk/test/browser/browser_about_cache.js @@ -0,0 +1,71 @@ +"use strict"; + +/** + * Open a dummy page, then open about:cache and verify the opened page shows up in the cache. + */ +add_task(function*() { + const kRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", + "https://example.com/"); + const kTestPage = kRoot + "dummy.html"; + // Open the dummy page to get it cached. + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kTestPage, true); + yield BrowserTestUtils.removeTab(tab); + + tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:cache", true); + let expectedPageCheck = function(uri) { + info("Saw load for " + uri); + // Can't easily use searchParms and new URL() because it's an about: URI... + return uri.startsWith("about:cache?") && uri.includes("storage=disk"); + }; + let diskPageLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, expectedPageCheck); + yield ContentTask.spawn(tab.linkedBrowser, null, function() { + ok(!content.document.nodePrincipal.isSystemPrincipal, + "about:cache should not have system principal"); + let principalURI = content.document.nodePrincipal.URI; + let channel = content.document.docShell.currentDocumentChannel; + ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null."); + is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location"); + let links = [... content.document.querySelectorAll("a[href*=disk]")]; + is(links.length, 1, "Should have 1 link to the disk entries"); + links[0].click(); + }); + yield diskPageLoaded; + info("about:cache disk subpage loaded"); + + expectedPageCheck = function(uri) { + info("Saw load for " + uri); + return uri.startsWith("about:cache-entry") && uri.includes("dummy.html"); + }; + let triggeringURISpec = tab.linkedBrowser.currentURI.spec; + let entryLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, expectedPageCheck); + yield ContentTask.spawn(tab.linkedBrowser, kTestPage, function(kTestPage) { + ok(!content.document.nodePrincipal.isSystemPrincipal, + "about:cache with query params should still not have system principal"); + let principalURI = content.document.nodePrincipal.URI; + is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location"); + let channel = content.document.docShell.currentDocumentChannel; + principalURI = channel.loadInfo.triggeringPrincipal.URI; + is(principalURI && principalURI.spec, "about:cache", "Triggering principal matches previous location"); + ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null."); + let links = [... content.document.querySelectorAll("a[href*='" + kTestPage + "']")]; + is(links.length, 1, "Should have 1 link to the entry for " + kTestPage); + links[0].click(); + }); + yield entryLoaded; + info("about:cache entry loaded"); + + + yield ContentTask.spawn(tab.linkedBrowser, triggeringURISpec, function(triggeringURISpec) { + ok(!content.document.nodePrincipal.isSystemPrincipal, + "about:cache-entry should also not have system principal"); + let principalURI = content.document.nodePrincipal.URI; + is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location"); + let channel = content.document.docShell.currentDocumentChannel; + principalURI = channel.loadInfo.triggeringPrincipal.URI; + is(principalURI && principalURI.spec, triggeringURISpec, "Triggering principal matches previous location"); + ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null."); + ok(content.document.querySelectorAll("th").length, + "Should have several table headers with data."); + }); + yield BrowserTestUtils.removeTab(tab); +});
--- a/netwerk/test/unit/test_be_conservative.js +++ b/netwerk/test/unit/test_be_conservative.js @@ -76,28 +76,37 @@ class InputStreamCallback { } } class TLSServerSecurityObserver { constructor(input, output) { this.input = input; this.output = output; this.callbacks = []; + this.stopped = false; } onHandshakeDone(socket, status) { do_print("TLS handshake done"); do_print(`TLS version used: ${status.tlsVersionUsed}`); + if (this.stopped) { + do_print("handshake done callback stopped - closing streams and bailing"); + this.input.close(); + this.output.close(); + return; + } + let callback = new InputStreamCallback(this.output); this.callbacks.push(callback); this.input.asyncWait(callback, 0, 0, Services.tm.currentThread); } stop() { + this.stopped = true; this.callbacks.forEach((callback) => { callback.stop(); }); } } class ServerSocketListener { constructor() {
--- a/services/sync/modules/bookmark_validator.js +++ b/services/sync/modules/bookmark_validator.js @@ -3,20 +3,21 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const Cu = Components.utils; Cu.import("resource://gre/modules/PlacesUtils.jsm"); Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Timer.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - this.EXPORTED_SYMBOLS = ["BookmarkValidator", "BookmarkProblemData"]; const LEFT_PANE_ROOT_ANNO = "PlacesOrganizer/OrganizerFolder"; const LEFT_PANE_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery"; // Indicates if a local bookmark tree node should be excluded from syncing. function isNodeIgnored(treeNode) { return treeNode.annos && treeNode.annos.some(anno => anno.name == LEFT_PANE_ROOT_ANNO || @@ -209,23 +210,27 @@ class BookmarkValidator { let concreteItem = recordMap.get(concreteId); if (!concreteItem) { continue; } entry.concrete = concreteItem; } } - createClientRecordsFromTree(clientTree) { + async createClientRecordsFromTree(clientTree) { // Iterate over the treeNode, converting it to something more similar to what // the server stores. let records = []; let recordsByGuid = new Map(); let syncedRoots = SYNCED_ROOTS; - function traverse(treeNode, synced) { + let yieldCounter = 0; + async function traverse(treeNode, synced) { + if (++yieldCounter % 50 === 0) { + await new Promise(resolve => setTimeout(resolve, 50)); + } if (!synced) { synced = syncedRoots.includes(treeNode.guid); } else if (isNodeIgnored(treeNode)) { synced = false; } let guid = PlacesSyncUtils.bookmarks.guidToSyncId(treeNode.guid); let itemType = "item"; treeNode.ignored = !synced; @@ -275,24 +280,24 @@ class BookmarkValidator { // We want to use the "real" guid here. recordsByGuid.set(treeNode.guid, treeNode); if (treeNode.type === "folder") { treeNode.childGUIDs = []; if (!treeNode.children) { treeNode.children = []; } for (let child of treeNode.children) { - traverse(child, synced); + await traverse(child, synced); child.parent = treeNode; child.parentid = guid; treeNode.childGUIDs.push(child.guid); } } } - traverse(clientTree, false); + await traverse(clientTree, false); clientTree.id = "places"; this._followQueries(recordsByGuid); return records; } /** * Process the server-side list. Mainly this builds the records into a tree, * but it also records information about problems, and produces arrays of the @@ -313,27 +318,28 @@ class BookmarkValidator { * - root: Root of the server-side bookmark tree. Has the same properties as * above. * - deletedRecords: As above, but only contains items that the server sent * where it also sent indication that the item should be deleted. * - problemData: a BookmarkProblemData object, with the caveat that * the fields describing client/server relationship will not have been filled * out yet. */ - inspectServerRecords(serverRecords) { + async inspectServerRecords(serverRecords) { let deletedItemIds = new Set(); let idToRecord = new Map(); let deletedRecords = []; let folders = []; let problemData = new BookmarkProblemData(); let resultRecords = []; + let yieldCounter = 0; for (let record of serverRecords) { if (!record.id) { ++problemData.missingIDs; continue; } if (record.deleted) { deletedItemIds.add(record.id); } else if (idToRecord.has(record.id)) { @@ -362,16 +368,19 @@ class BookmarkValidator { // The children array stores special guids as their local guid values, // e.g. 'menu________' instead of 'menu', but all other parts of the // serverside bookmark info stores it as the special value ('menu'). record.childGUIDs = record.children; record.children = record.children.map(childID => { return PlacesSyncUtils.bookmarks.guidToSyncId(childID); }); } + if (++yieldCounter % 50 === 0) { + await new Promise(resolve => setTimeout(resolve, 50)); + } } for (let deletedId of deletedItemIds) { let record = idToRecord.get(deletedId); if (record && !record.isDeleted) { deletedRecords.push(record); record.isDeleted = true; } @@ -602,20 +611,20 @@ class BookmarkValidator { * * Returns the same data as described in the inspectServerRecords comment, * with the following additional fields. * - clientRecords: an array of client records in a similar format to * the .records (ie, server records) entry. * - problemData is the same as for inspectServerRecords, except all properties * will be filled out. */ - compareServerWithClient(serverRecords, clientTree) { + async compareServerWithClient(serverRecords, clientTree) { - let clientRecords = this.createClientRecordsFromTree(clientTree); - let inspectionInfo = this.inspectServerRecords(serverRecords); + let clientRecords = await this.createClientRecordsFromTree(clientTree); + let inspectionInfo = await this.inspectServerRecords(serverRecords); inspectionInfo.clientRecords = clientRecords; // Mainly do this to remove deleted items and normalize child guids. serverRecords = inspectionInfo.records; let problemData = inspectionInfo.problemData; this._validateClient(problemData, clientRecords); @@ -747,33 +756,30 @@ class BookmarkValidator { }; let resp = collection.getBatched(); if (!resp.success) { throw resp; } return items; } - validate(engine) { - let self = this; - return Task.spawn(function*() { - let start = Date.now(); - let clientTree = yield PlacesUtils.promiseBookmarksTree("", { - includeItemIds: true - }); - let serverState = self._getServerState(engine); - let serverRecordCount = serverState.length; - let result = self.compareServerWithClient(serverState, clientTree); - let end = Date.now(); - let duration = end - start; - return { - duration, - version: self.version, - problems: result.problemData, - recordCount: serverRecordCount - }; + async validate(engine) { + let start = Date.now(); + let clientTree = await PlacesUtils.promiseBookmarksTree("", { + includeItemIds: true }); + let serverState = this._getServerState(engine); + let serverRecordCount = serverState.length; + let result = await this.compareServerWithClient(serverState, clientTree); + let end = Date.now(); + let duration = end - start; + return { + duration, + version: this.version, + problems: result.problemData, + recordCount: serverRecordCount + }; } } BookmarkValidator.prototype.version = BOOKMARK_VALIDATOR_VERSION;
--- a/services/sync/modules/engines.js +++ b/services/sync/modules/engines.js @@ -728,17 +728,18 @@ Engine.prototype = { * must have a `validate(engine)` method that returns a promise to an object * with a getSummary method). Otherwise return null. */ getValidator() { return null; }, finalize() { - // Ensure the tracker finishes persisting changed IDs to disk. + // Persist all pending tracked changes to disk. + this._tracker._saveChangedIDs(); Async.promiseSpinningly(this._tracker._storage.finalize()); }, }; this.SyncEngine = function SyncEngine(name, service) { Engine.call(this, name || "SyncEngine", service); this.loadToFetch();
--- a/services/sync/services-sync.js +++ b/services/sync/services-sync.js @@ -72,9 +72,9 @@ pref("services.sync.telemetry.maxPayload pref("services.sync.validation.interval", 86400); // 24 hours in seconds // We only run validation `services.sync.validation.percentageChance` percent of // the time, even if it's been the right amount of time since the last validation, // and you meet the maxRecord checks. pref("services.sync.validation.percentageChance", 10); // We won't validate an engine if it has more than this many records on the server. -pref("services.sync.validation.maxRecords", 100); +pref("services.sync.validation.maxRecords", 1000);
--- a/services/sync/tests/unit/sync_ping_schema.json +++ b/services/sync/tests/unit/sync_ping_schema.json @@ -71,17 +71,17 @@ "version": { "type": "string" } } }, "engine": { "required": ["name"], "additionalProperties": false, "properties": { "failureReason": { "$ref": "#/definitions/error" }, - "name": { "enum": ["addons", "bookmarks", "clients", "forms", "history", "passwords", "prefs", "tabs"] }, + "name": { "enum": ["addons", "bookmarks", "clients", "forms", "history", "passwords", "prefs", "tabs", "extension-storage"] }, "took": { "type": "integer", "minimum": 1 }, "status": { "type": "string" }, "incoming": { "type": "object", "additionalProperties": false, "anyOf": [ {"required": ["applied"]}, {"required": ["failed"]},
--- a/services/sync/tests/unit/test_bookmark_duping.js +++ b/services/sync/tests/unit/test_bookmark_duping.js @@ -95,17 +95,17 @@ async function promiseNoLocalItem(guid) // and while we are here ensure the places cache doesn't still have it. await Assert.rejects(PlacesUtils.promiseItemId(guid)); } async function validate(collection, expectedFailures = []) { let validator = new BookmarkValidator(); let records = collection.payloads(); - let problems = validator.inspectServerRecords(records).problemData; + let { problemData: problems } = await validator.inspectServerRecords(records); // all non-zero problems. let summary = problems.getSummary().filter(prob => prob.count != 0); // split into 2 arrays - expected and unexpected. let isInExpectedFailures = elt => { for (let i = 0; i < expectedFailures.length; i++) { if (elt.name == expectedFailures[i].name && elt.count == expectedFailures[i].count) { return true;
--- a/services/sync/tests/unit/test_bookmark_validator.js +++ b/services/sync/tests/unit/test_bookmark_validator.js @@ -1,125 +1,121 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ Components.utils.import("resource://services-sync/bookmark_validator.js"); Components.utils.import("resource://services-sync/util.js"); -function inspectServerRecords(data) { - return new BookmarkValidator().inspectServerRecords(data); +async function inspectServerRecords(data) { + let validator = new BookmarkValidator(); + return validator.inspectServerRecords(data); } -add_test(function test_isr_rootOnServer() { - let c = inspectServerRecords([{ +async function compareServerWithClient(server, client) { + let validator = new BookmarkValidator(); + return validator.compareServerWithClient(server, client); +} + +add_task(async function test_isr_rootOnServer() { + let c = await inspectServerRecords([{ id: "places", type: "folder", children: [], }]); ok(c.problemData.rootOnServer); - run_next_test(); }); -add_test(function test_isr_empty() { - let c = inspectServerRecords([]); +add_task(async function test_isr_empty() { + let c = await inspectServerRecords([]); ok(!c.problemData.rootOnServer); notEqual(c.root, null); - run_next_test(); }); -add_test(function test_isr_cycles() { - let c = inspectServerRecords([ +add_task(async function test_isr_cycles() { + let c = (await inspectServerRecords([ {id: "C", type: "folder", children: ["A", "B"], parentid: "places"}, {id: "A", type: "folder", children: ["B"], parentid: "B"}, {id: "B", type: "folder", children: ["A"], parentid: "A"}, - ]).problemData; + ])).problemData; equal(c.cycles.length, 1); ok(c.cycles[0].indexOf("A") >= 0); ok(c.cycles[0].indexOf("B") >= 0); - run_next_test(); }); -add_test(function test_isr_orphansMultiParents() { - let c = inspectServerRecords([ +add_task(async function test_isr_orphansMultiParents() { + let c = (await inspectServerRecords([ { id: "A", type: "bookmark", parentid: "D" }, { id: "B", type: "folder", parentid: "places", children: ["A"]}, { id: "C", type: "folder", parentid: "places", children: ["A"]}, - ]).problemData; + ])).problemData; deepEqual(c.orphans, [{ id: "A", parent: "D" }]); equal(c.multipleParents.length, 1) ok(c.multipleParents[0].parents.indexOf("B") >= 0); ok(c.multipleParents[0].parents.indexOf("C") >= 0); - run_next_test(); }); -add_test(function test_isr_orphansMultiParents2() { - let c = inspectServerRecords([ +add_task(async function test_isr_orphansMultiParents2() { + let c = (await inspectServerRecords([ { id: "A", type: "bookmark", parentid: "D" }, { id: "B", type: "folder", parentid: "places", children: ["A"]}, - ]).problemData; + ])).problemData; equal(c.orphans.length, 1); equal(c.orphans[0].id, "A"); equal(c.multipleParents.length, 0); - run_next_test(); }); -add_test(function test_isr_deletedParents() { - let c = inspectServerRecords([ +add_task(async function test_isr_deletedParents() { + let c = (await inspectServerRecords([ { id: "A", type: "bookmark", parentid: "B" }, { id: "B", type: "folder", parentid: "places", children: ["A"]}, { id: "B", type: "item", deleted: true}, - ]).problemData; - deepEqual(c.deletedParents, ["A"]) - run_next_test(); + ])).problemData; + deepEqual(c.deletedParents, ["A"]); }); -add_test(function test_isr_badChildren() { - let c = inspectServerRecords([ +add_task(async function test_isr_badChildren() { + let c = (await inspectServerRecords([ { id: "A", type: "bookmark", parentid: "places", children: ["B", "C"] }, { id: "C", type: "bookmark", parentid: "A" } - ]).problemData; + ])).problemData; deepEqual(c.childrenOnNonFolder, ["A"]) deepEqual(c.missingChildren, [{parent: "A", child: "B"}]); deepEqual(c.parentNotFolder, ["C"]); - run_next_test(); }); -add_test(function test_isr_parentChildMismatches() { - let c = inspectServerRecords([ +add_task(async function test_isr_parentChildMismatches() { + let c = (await inspectServerRecords([ { id: "A", type: "folder", parentid: "places", children: [] }, { id: "B", type: "bookmark", parentid: "A" } - ]).problemData; + ])).problemData; deepEqual(c.parentChildMismatches, [{parent: "A", child: "B"}]); - run_next_test(); }); -add_test(function test_isr_duplicatesAndMissingIDs() { - let c = inspectServerRecords([ +add_task(async function test_isr_duplicatesAndMissingIDs() { + let c = (await inspectServerRecords([ {id: "A", type: "folder", parentid: "places", children: []}, {id: "A", type: "folder", parentid: "places", children: []}, {type: "folder", parentid: "places", children: []} - ]).problemData; + ])).problemData; equal(c.missingIDs, 1); deepEqual(c.duplicates, ["A"]); - run_next_test(); }); -add_test(function test_isr_duplicateChildren() { - let c = inspectServerRecords([ +add_task(async function test_isr_duplicateChildren() { + let c = (await inspectServerRecords([ {id: "A", type: "folder", parentid: "places", children: ["B", "B"]}, {id: "B", type: "bookmark", parentid: "A"}, - ]).problemData; + ])).problemData; deepEqual(c.duplicateChildren, ["A"]); - run_next_test(); }); -// Each compareServerWithClient test mutates these, so we can't just keep them +// Each compareServerWithClient test mutates these, so we can"t just keep them // global function getDummyServerAndClient() { let server = [ { id: "menu", parentid: "places", type: "folder", parentName: "", @@ -179,72 +175,68 @@ function getDummyServerAndClient() { ] } ] }; return {server, client}; } -add_test(function test_cswc_valid() { +add_task(async function test_cswc_valid() { let {server, client} = getDummyServerAndClient(); - let c = new BookmarkValidator().compareServerWithClient(server, client).problemData; + let c = (await compareServerWithClient(server, client)).problemData; equal(c.clientMissing.length, 0); equal(c.serverMissing.length, 0); equal(c.differences.length, 0); - run_next_test(); }); -add_test(function test_cswc_serverMissing() { +add_task(async function test_cswc_serverMissing() { let {server, client} = getDummyServerAndClient(); // remove c server.pop(); server[0].children.pop(); - let c = new BookmarkValidator().compareServerWithClient(server, client).problemData; + let c = (await compareServerWithClient(server, client)).problemData; deepEqual(c.serverMissing, ["cccccccccccc"]); equal(c.clientMissing.length, 0); deepEqual(c.structuralDifferences, [{id: "menu", differences: ["childGUIDs"]}]); - run_next_test(); }); -add_test(function test_cswc_clientMissing() { +add_task(async function test_cswc_clientMissing() { let {server, client} = getDummyServerAndClient(); client.children[0].children.pop(); - let c = new BookmarkValidator().compareServerWithClient(server, client).problemData; + let c = (await compareServerWithClient(server, client)).problemData; deepEqual(c.clientMissing, ["cccccccccccc"]); equal(c.serverMissing.length, 0); deepEqual(c.structuralDifferences, [{id: "menu", differences: ["childGUIDs"]}]); - run_next_test(); }); -add_test(function test_cswc_differences() { +add_task(async function test_cswc_differences() { { let {server, client} = getDummyServerAndClient(); client.children[0].children[0].title = "asdf"; - let c = new BookmarkValidator().compareServerWithClient(server, client).problemData; + let c = (await compareServerWithClient(server, client)).problemData; equal(c.clientMissing.length, 0); equal(c.serverMissing.length, 0); deepEqual(c.differences, [{id: "bbbbbbbbbbbb", differences: ["title"]}]); } { let {server, client} = getDummyServerAndClient(); server[2].type = "bookmark"; - let c = new BookmarkValidator().compareServerWithClient(server, client).problemData; + let c = (await compareServerWithClient(server, client)).problemData; equal(c.clientMissing.length, 0); equal(c.serverMissing.length, 0); deepEqual(c.differences, [{id: "cccccccccccc", differences: ["type"]}]); } - run_next_test(); }); -add_test(function test_cswc_serverUnexpected() { +add_task(async function test_cswc_serverUnexpected() { let {server, client} = getDummyServerAndClient(); client.children.push({ "guid": "dddddddddddd", "title": "", "id": 2000, "annos": [{ "name": "places/excludeFromBackup", "flags": 0, @@ -286,41 +278,41 @@ add_test(function test_cswc_serverUnexpe id: "eeeeeeeeeeee", parentid: "dddddddddddd", parentName: "", title: "History", type: "query", bmkUri: "place:type=3&sort=4" }); - let c = new BookmarkValidator().compareServerWithClient(server, client).problemData; + let c = (await compareServerWithClient(server, client)).problemData; equal(c.clientMissing.length, 0); equal(c.serverMissing.length, 0); equal(c.serverUnexpected.length, 2); deepEqual(c.serverUnexpected, ["dddddddddddd", "eeeeeeeeeeee"]); - run_next_test(); }); -function validationPing(server, client, duration) { - return wait_for_ping(function() { - // fake this entirely - Svc.Obs.notify("weave:service:sync:start"); - Svc.Obs.notify("weave:engine:sync:start", null, "bookmarks"); - Svc.Obs.notify("weave:engine:sync:finish", null, "bookmarks"); - let validator = new BookmarkValidator(); - let data = { - // We fake duration and version just so that we can verify they're passed through. - duration, - version: validator.version, - recordCount: server.length, - problems: validator.compareServerWithClient(server, client).problemData, - }; - Svc.Obs.notify("weave:engine:validate:finish", data, "bookmarks"); - Svc.Obs.notify("weave:service:sync:finish"); - }, true); // Allow "failing" pings, since having validation info indicates failure. +async function validationPing(server, client, duration) { + let pingPromise = wait_for_ping(() => {}, true); // Allow "failing" pings, since having validation info indicates failure. + // fake this entirely + Svc.Obs.notify("weave:service:sync:start"); + Svc.Obs.notify("weave:engine:sync:start", null, "bookmarks"); + Svc.Obs.notify("weave:engine:sync:finish", null, "bookmarks"); + let validator = new BookmarkValidator(); + let {problemData} = await validator.compareServerWithClient(server, client); + let data = { + // We fake duration and version just so that we can verify they"re passed through. + duration, + version: validator.version, + recordCount: server.length, + problems: problemData, + }; + Svc.Obs.notify("weave:engine:validate:finish", data, "bookmarks"); + Svc.Obs.notify("weave:service:sync:finish"); + return pingPromise; } add_task(async function test_telemetry_integration() { let {server, client} = getDummyServerAndClient(); // remove "c" server.pop(); server[0].children.pop(); const duration = 50; @@ -336,12 +328,8 @@ add_task(async function test_telemetry_i equal(bme.validation.version, new BookmarkValidator().version); deepEqual(bme.validation.problems, [ { name: "badClientRoots", count: 3 }, { name: "sdiff:childGUIDs", count: 1 }, { name: "serverMissing", count: 1 }, { name: "structuralDifferences", count: 1 }, ]); }); - -function run_test() { - run_next_test(); -}
--- a/services/sync/tests/unit/test_engine.js +++ b/services/sync/tests/unit/test_engine.js @@ -18,17 +18,18 @@ SteamStore.prototype = { this.wasWiped = true; } }; function SteamTracker(name, engine) { Tracker.call(this, name || "Steam", engine); } SteamTracker.prototype = { - __proto__: Tracker.prototype + __proto__: Tracker.prototype, + persistChangedIDs: false, }; function SteamEngine(name, service) { Engine.call(this, name, service); this.wasReset = false; this.wasSynced = false; } SteamEngine.prototype = {
--- a/services/sync/tests/unit/test_telemetry.js +++ b/services/sync/tests/unit/test_telemetry.js @@ -28,17 +28,18 @@ SteamStore.prototype = { __proto__: Store.prototype, }; function SteamTracker(name, engine) { Tracker.call(this, name || "Steam", engine); } SteamTracker.prototype = { - __proto__: Tracker.prototype + __proto__: Tracker.prototype, + persistChangedIDs: false, }; function SteamEngine(service) { Engine.call(this, "steam", service); } SteamEngine.prototype = { __proto__: Engine.prototype,
--- a/services/sync/tps/extensions/tps/resource/tps.jsm +++ b/services/sync/tps/extensions/tps/resource/tps.jsm @@ -130,19 +130,16 @@ var TPS = { _init: function TPS__init() { this.delayAutoSync(); OBSERVER_TOPICS.forEach(function(aTopic) { Services.obs.addObserver(this, aTopic, true); }, this); - // Configure some logging prefs for Sync itself. - Weave.Svc.Prefs.set("log.appender.dump", "Debug"); - /* global Authentication */ Cu.import("resource://tps/auth/fxaccounts.jsm", module); }, DumpError(msg, exc = null) { this._errors++; let errInfo; if (exc) { @@ -619,17 +616,17 @@ var TPS = { let clientTree = Async.promiseSpinningly(PlacesUtils.promiseBookmarksTree("", { includeItemIds: true })); let serverRecords = getServerBookmarkState(); // We can't wait until catch to stringify this, since at that point it will have cycles. serverRecordDumpStr = JSON.stringify(serverRecords); let validator = new BookmarkValidator(); - let {problemData} = validator.compareServerWithClient(serverRecords, clientTree); + let {problemData} = Async.promiseSpinningly(validator.compareServerWithClient(serverRecords, clientTree)); for (let {name, count} of problemData.getSummary()) { // Exclude mobile showing up on the server hackily so that we don't // report it every time, see bug 1273234 and 1274394 for more information. if (name === "serverUnexpected" && problemData.serverUnexpected.indexOf("mobile") >= 0) { --count; } if (count) {
--- a/storage/mozStorageService.cpp +++ b/storage/mozStorageService.cpp @@ -9,18 +9,16 @@ #include "mozStorageService.h" #include "mozStorageConnection.h" #include "nsAutoPtr.h" #include "nsCollationCID.h" #include "nsEmbedCID.h" #include "nsThreadUtils.h" #include "mozStoragePrivateHelpers.h" -#include "nsILocale.h" -#include "nsILocaleService.h" #include "nsIXPConnect.h" #include "nsIObserverService.h" #include "nsIPropertyBag2.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" #include "mozilla/LateWriteChecks.h" #include "mozIStorageCompletionCallback.h" #include "mozIStoragePendingStatement.h" @@ -603,37 +601,24 @@ Service::localeCompareStrings(const nsAS nsICollation * Service::getLocaleCollation() { mMutex.AssertCurrentThreadOwns(); if (mLocaleCollation) return mLocaleCollation; - nsCOMPtr<nsILocaleService> svc(do_GetService(NS_LOCALESERVICE_CONTRACTID)); - if (!svc) { - NS_WARNING("Could not get locale service"); - return nullptr; - } - - nsCOMPtr<nsILocale> appLocale; - nsresult rv = svc->GetApplicationLocale(getter_AddRefs(appLocale)); - if (NS_FAILED(rv)) { - NS_WARNING("Could not get application locale"); - return nullptr; - } - nsCOMPtr<nsICollationFactory> collFact = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); if (!collFact) { NS_WARNING("Could not create collation factory"); return nullptr; } - rv = collFact->CreateCollation(appLocale, getter_AddRefs(mLocaleCollation)); + nsresult rv = collFact->CreateCollation(getter_AddRefs(mLocaleCollation)); if (NS_FAILED(rv)) { NS_WARNING("Could not create collation"); return nullptr; } return mLocaleCollation; }
--- a/storage/test/unit/test_locale_collation.js +++ b/storage/test/unit/test_locale_collation.js @@ -227,21 +227,19 @@ function setup() { gStrings = readTestData(); initTableWithStrings(gStrings, getOpenedDatabase()); gUtf16Conn = createUtf16Database(); initTableWithStrings(gStrings, gUtf16Conn); - let localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"]. - getService(Ci.nsILocaleService); let collFact = Cc["@mozilla.org/intl/collation-factory;1"]. createInstance(Ci.nsICollationFactory); - gLocaleCollation = collFact.CreateCollation(localeSvc.getApplicationLocale()); + gLocaleCollation = collFact.CreateCollation(); } // Test Runs var gTests = [ { desc: "Case and accent sensitive UTF-8", run: () => runUtf8Test("locale_case_accent_sensitive")
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm +++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm @@ -37,16 +37,19 @@ XPCOMUtils.defineLazyModuleGetter(this, // some cases. Cu.permitCPOWsInScope(this); var gSendCharCount = 0; var gSynthesizeKeyCount = 0; var gSynthesizeCompositionCount = 0; var gSynthesizeCompositionChangeCount = 0; +const kAboutPageRegistrationContentScript = + "chrome://mochikit/content/tests/BrowserTestUtils/content-about-page-utils.js"; + this.BrowserTestUtils = { /** * Loads a page in a new tab, executes a Task and closes the tab. * * @param options * An object or string. * If this is a string it is the url to open and will be opened in the * currently active browser window. @@ -1263,9 +1266,67 @@ this.BrowserTestUtils = { return new Promise((resolve) => { addEventListener("MozAfterPaint", function onPaint() { removeEventListener("MozAfterPaint", onPaint); resolve(); }) }); }); }, + + _knownAboutPages: new Set(), + _loadedAboutContentScript: false, + /** + * Registers an about: page with particular flags in both the parent + * and any content processes. Returns a promise that resolves when + * registration is complete. + * + * @param registerCleanupFunction (Function) + * The test framework doesn't keep its cleanup stuff anywhere accessible, + * so the first argument is a reference to your cleanup registration + * function, allowing us to clean up after you if necessary. + * @param aboutModule (String) + * The name of the about page. + * @param pageURI (String) + * The URI the about: page should point to. + * @param flags (Number) + * The nsIAboutModule flags to use for registration. + * @returns Promise that resolves when registration has finished. + */ + registerAboutPage(registerCleanupFunction, aboutModule, pageURI, flags) { + // Return a promise that resolves when registration finished. + const kRegistrationMsgId = "browser-test-utils:about-registration:registered"; + let rv = this.waitForMessage(Services.ppmm, kRegistrationMsgId, msg => { + return msg.data == aboutModule; + }); + // Load a script that registers our page, then send it a message to execute the registration. + if (!this._loadedAboutContentScript) { + Services.ppmm.loadProcessScript(kAboutPageRegistrationContentScript, true); + this._loadedAboutContentScript = true; + registerCleanupFunction(this._removeAboutPageRegistrations.bind(this)); + } + Services.ppmm.broadcastAsyncMessage("browser-test-utils:about-registration:register", + {aboutModule, pageURI, flags}); + return rv.then(() => { + this._knownAboutPages.add(aboutModule); + }); + }, + + unregisterAboutPage(aboutModule) { + if (!this._knownAboutPages.has(aboutModule)) { + return Promise.reject(new Error("We don't think this about page exists!")); + } + const kUnregistrationMsgId = "browser-test-utils:about-registration:unregistered"; + let rv = this.waitForMessage(Services.ppmm, kUnregistrationMsgId, msg => { + return msg.data == aboutModule; + }); + Services.ppmm.broadcastAsyncMessage("browser-test-utils:about-registration:unregister", + aboutModule); + return rv.then(() => this._knownAboutPages.delete(aboutModule)); + }, + + *_removeAboutPageRegistrations() { + for (let aboutModule of this._knownAboutPages) { + yield this.unregisterAboutPage(aboutModule); + } + Services.ppmm.removeDelayedProcessScript(kAboutPageRegistrationContentScript); + }, };
new file mode 100644 --- /dev/null +++ b/testing/mochitest/BrowserTestUtils/content/content-about-page-utils.js @@ -0,0 +1,76 @@ +"use strict"; + +var {classes: Cc, interfaces: Ci, utils: Cu, manager: Cm} = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); + +function AboutPage(aboutHost, chromeURL, uriFlags) { + this.chromeURL = chromeURL; + this.aboutHost = aboutHost; + this.classID = Components.ID(generateUUID().number); + this.description = "BrowserTestUtils: " + aboutHost; + this.uriFlags = uriFlags; +} + +AboutPage.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), + getURIFlags(aURI) { // eslint-disable-line no-unused-vars + return this.uriFlags; + }, + + newChannel(aURI, aLoadInfo) { + let newURI = Services.io.newURI(this.chromeURL); + let channel = Services.io.newChannelFromURIWithLoadInfo(newURI, + aLoadInfo); + channel.originalURI = aURI; + + if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) { + channel.owner = null; + } + return channel; + }, + + createInstance(outer, iid) { + if (outer !== null) { + throw Cr.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(iid); + }, + + register() { + Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory( + this.classID, this.description, + "@mozilla.org/network/protocol/about;1?what=" + this.aboutHost, this); + }, + + unregister() { + Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory( + this.classID, this); + } +}; + +const gRegisteredPages = new Map(); + +addMessageListener("browser-test-utils:about-registration:register", msg => { + let {aboutModule, pageURI, flags} = msg.data; + if (gRegisteredPages.has(aboutModule)) { + gRegisteredPages.get(aboutModule).unregister(); + } + let moduleObj = new AboutPage(aboutModule, pageURI, flags); + moduleObj.register(); + gRegisteredPages.set(aboutModule, moduleObj); + sendAsyncMessage("browser-test-utils:about-registration:registered", aboutModule); +}); + +addMessageListener("browser-test-utils:about-registration:unregister", msg => { + let aboutModule = msg.data; + let moduleObj = gRegisteredPages.get(aboutModule); + if (moduleObj) { + moduleObj.unregister(); + gRegisteredPages.delete(aboutModule); + } + sendAsyncMessage("browser-test-utils:about-registration:unregistered", aboutModule); +});
--- a/testing/mochitest/jar.mn +++ b/testing/mochitest/jar.mn @@ -37,10 +37,11 @@ mochikit.jar: content/tests/SimpleTest/TestRunner.js (tests/SimpleTest/TestRunner.js) content/tests/SimpleTest/iframe-between-tests.html (tests/SimpleTest/iframe-between-tests.html) content/tests/SimpleTest/WindowSnapshot.js (tests/SimpleTest/WindowSnapshot.js) content/tests/SimpleTest/MockObjects.js (tests/SimpleTest/MockObjects.js) content/tests/SimpleTest/NativeKeyCodes.js (tests/SimpleTest/NativeKeyCodes.js) content/tests/SimpleTest/paint_listener.js (tests/SimpleTest/paint_listener.js) content/tests/SimpleTest/docshell_helpers.js (../../docshell/test/chrome/docshell_helpers.js) content/tests/BrowserTestUtils/content-task.js (BrowserTestUtils/content/content-task.js) + content/tests/BrowserTestUtils/content-about-page-utils.js (BrowserTestUtils/content/content-about-page-utils.js) content/tests/BrowserTestUtils/content-utils.js (BrowserTestUtils/content/content-utils.js)
--- a/testing/mochitest/tests/browser/browser.ini +++ b/testing/mochitest/tests/browser/browser.ini @@ -1,16 +1,18 @@ [DEFAULT] support-files = head.js [browser_browserLoaded_content_loaded.js] [browser_add_task.js] [browser_async.js] [browser_BrowserTestUtils.js] +support-files = + dummy.html [browser_head.js] [browser_pass.js] [browser_parameters.js] [browser_popupNode.js] [browser_popupNode_check.js] [browser_privileges.js] [browser_sanityException.js] [browser_sanityException2.js]
--- a/testing/mochitest/tests/browser/browser_BrowserTestUtils.js +++ b/testing/mochitest/tests/browser/browser_BrowserTestUtils.js @@ -42,8 +42,29 @@ add_task(function* () { cancelled = yield BrowserTestUtils.synthesizeMouseAtCenter("body > div", { type: "mouseup" }, browser); details = yield getLastEventDetails(browser); is(details, "div,40,84", "synthesizeMouseAtCenter mouseup with complex selector"); ok(!cancelled, "synthesizeMouseAtCenter mouseup with complex selector cancelled"); gBrowser.removeTab(tab); }); + +add_task(function* () { + yield BrowserTestUtils.registerAboutPage( + registerCleanupFunction, "about-pages-are-cool", + getRootDirectory(gTestPath) + "dummy.html", 0); + let tab = yield BrowserTestUtils.openNewForegroundTab( + gBrowser, "about:about-pages-are-cool", true); + ok(tab, "Successfully created an about: page and loaded it."); + yield BrowserTestUtils.removeTab(tab); + try { + yield BrowserTestUtils.unregisterAboutPage("about-pages-are-cool"); + ok(true, "Successfully unregistered the about page."); + } catch (ex) { + ok(false, "Should not throw unregistering a known about: page"); + } + yield BrowserTestUtils.unregisterAboutPage("random-other-about-page").then(() => { + ok(false, "Should not have succeeded unregistering an unknown about: page."); + }, () => { + ok(true, "Should have returned a rejected promise trying to unregister an unknown about page"); + }); +});
new file mode 100644 --- /dev/null +++ b/testing/mochitest/tests/browser/dummy.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> + <title>This is a dummy page</title> + <meta charset="utf-8"> + <body>This is a dummy page</body> +</html>
--- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -7,30 +7,16 @@ TARGET_DEPTH = $(DEPTH) include $(topsrcdir)/build/binary-location.mk SYMBOLS_PATH := --symbols-path=$(DIST)/crashreporter-symbols ifndef TEST_PACKAGE_NAME TEST_PACKAGE_NAME := $(ANDROID_PACKAGE_NAME) endif -# Linking xul-gtest.dll takes too long, so we disable GTest on -# Windows PGO builds (bug 1028035). -ifneq (1_WINNT,$(MOZ_PGO)_$(OS_ARCH)) -BUILD_GTEST=1 -endif - -ifneq (browser,$(MOZ_BUILD_APP)) -BUILD_GTEST= -endif - -ifndef COMPILE_ENVIRONMENT -BUILD_GTEST= -endif - ifndef NO_FAIL_ON_TEST_ERRORS define check_test_error_internal @errors=`grep 'TEST-UNEXPECTED-' $@.log` ;\ if test "$$errors" ; then \ echo '$@ failed:'; \ echo "$$errors"; \ $(if $(1),echo $(1);) \ exit 1; \ @@ -147,17 +133,17 @@ TEST_PKGS := \ cppunittest \ mochitest \ reftest \ talos \ web-platform \ xpcshell \ $(NULL) -ifdef BUILD_GTEST +ifdef LINK_GTEST_DURING_COMPILE stage-all: stage-gtest TEST_PKGS += gtest endif PKG_ARG = --$(1) '$(PKG_BASENAME).$(1).tests.zip' test-packages-manifest: @rm -f $(MOZ_TEST_PACKAGES_FILE) @@ -213,18 +199,16 @@ stage-mochitest: make-stage-dir ifeq ($(MOZ_BUILD_APP),mobile/android) $(MAKE) -C $(DEPTH)/testing/mochitest stage-package endif stage-jstests: make-stage-dir $(MAKE) -C $(DEPTH)/js/src/tests stage-package stage-gtest: make-stage-dir -# FIXME: (bug 1200311) We should be generating the gtest xul as part of the build. - $(MAKE) -C $(DEPTH)/testing/gtest gtest $(NSINSTALL) -D $(PKG_STAGE)/gtest/gtest_bin cp -RL $(DIST)/bin/gtest $(PKG_STAGE)/gtest/gtest_bin cp -RL $(DEPTH)/_tests/gtest $(PKG_STAGE) cp $(topsrcdir)/testing/gtest/rungtests.py $(PKG_STAGE)/gtest cp $(DIST)/bin/dependentlibs.list.gtest $(PKG_STAGE)/gtest cp $(DEPTH)/mozinfo.json $(PKG_STAGE)/gtest stage-android: make-stage-dir
--- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -61491,17 +61491,17 @@ {} ] ], "web-animations/animation-model/animation-types/property-types.js": [ [ {} ] ], - "web-animations/resources/effect-easing-tests.js": [ + "web-animations/resources/easing-tests.js": [ [ {} ] ], "web-animations/resources/keyframe-utils.js": [ [ {} ] @@ -120073,28 +120073,40 @@ ] ], "web-animations/animation-model/keyframe-effects/effect-value-context.html": [ [ "/web-animations/animation-model/keyframe-effects/effect-value-context.html", {} ] ], + "web-animations/animation-model/keyframe-effects/effect-value-overlapping-keyframes.html": [ + [ + "/web-animations/animation-model/keyframe-effects/effect-value-overlapping-keyframes.html", + {} + ] + ], + "web-animations/animation-model/keyframe-effects/effect-value-transformed-distance.html": [ + [ + "/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance.html", + {} + ] + ], + "web-animations/animation-model/keyframe-effects/effect-value-visibility.html": [ + [ + "/web-animations/animation-model/keyframe-effects/effect-value-visibility.html", + {} + ] + ], "web-animations/animation-model/keyframe-effects/spacing-keyframes.html": [ [ "/web-animations/animation-model/keyframe-effects/spacing-keyframes.html", {} ] ], - "web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html": [ - [ - "/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html", - {} - ] - ], "web-animations/interfaces/Animatable/animate.html": [ [ "/web-animations/interfaces/Animatable/animate.html", {} ] ], "web-animations/interfaces/Animation/cancel.html": [ [ @@ -120283,22 +120295,16 @@ ] ], "web-animations/interfaces/KeyframeEffect/copy-contructor.html": [ [ "/web-animations/interfaces/KeyframeEffect/copy-contructor.html", {} ] ], - "web-animations/interfaces/KeyframeEffect/effect-easing.html": [ - [ - "/web-animations/interfaces/KeyframeEffect/effect-easing.html", - {} - ] - ], "web-animations/interfaces/KeyframeEffect/getComputedTiming.html": [ [ "/web-animations/interfaces/KeyframeEffect/getComputedTiming.html", {} ] ], "web-animations/interfaces/KeyframeEffect/iterationComposite.html": [ [ @@ -120391,16 +120397,22 @@ ] ], "web-animations/timing-model/animations/updating-the-finished-state.html": [ [ "/web-animations/timing-model/animations/updating-the-finished-state.html", {} ] ], + "web-animations/timing-model/time-transformations/transformed-progress.html": [ + [ + "/web-animations/timing-model/time-transformations/transformed-progress.html", + {} + ] + ], "webaudio/the-audio-api/the-audiobuffer-interface/idl-test.html": [ [ "/webaudio/the-audio-api/the-audiobuffer-interface/idl-test.html", {} ] ], "webaudio/the-audio-api/the-audiodestinationnode-interface/idl-test.html": [ [ @@ -203720,26 +203732,34 @@ "web-animations/animation-model/combining-effects/effect-composition.html": [ "8ac06085132d822e908d48de4c1109b66323f19f", "testharness" ], "web-animations/animation-model/keyframe-effects/effect-value-context.html": [ "10d9ee521240475a1729c2facfcea8b50342614e", "testharness" ], + "web-animations/animation-model/keyframe-effects/effect-value-overlapping-keyframes.html": [ + "a79db70a385ad767263f285c9401b66611087e42", + "testharness" + ], + "web-animations/animation-model/keyframe-effects/effect-value-transformed-distance.html": [ + "59e86254c8c118bd30b5c6742cfeaceba783eaee", + "testharness" + ], + "web-animations/animation-model/keyframe-effects/effect-value-visibility.html": [ + "b01c7c5145c183fdca80dec4ca1966b0f72a7003", + "testharness" + ], "web-animations/animation-model/keyframe-effects/spacing-keyframes.html": [ "318bc877791852b0829a3e10cb19e2a20a15adab", "testharness" ], - "web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html": [ - "b87824689825406a384d2e8afeac54c790ed16e0", - "testharness" - ], "web-animations/interfaces/Animatable/animate.html": [ - "d2d57b1fe0bd5b6da4c44c569ff7dcf802298919", + "d076cdc3862c8a178d69d44cfe422f4e48b0649a", "testharness" ], "web-animations/interfaces/Animation/cancel.html": [ "a28589129c6a2665295695f786b7beb2dd887fb3", "testharness" ], "web-animations/interfaces/Animation/constructor.html": [ "20604949fc295efc398e297b9e4f755a116f0fbb", @@ -203805,17 +203825,17 @@ "ab150d71daf36949d4d6804033e19c734a68552d", "testharness" ], "web-animations/interfaces/AnimationEffectTiming/duration.html": [ "2e0f0a270b8acd3d345732327ee2eabd32bdb2b2", "testharness" ], "web-animations/interfaces/AnimationEffectTiming/easing.html": [ - "e1055f83a2774e4c406b813cfb19d3ea4db83970", + "5d21bb3ae43da1226f9510595b47b452b3b8f223", "testharness" ], "web-animations/interfaces/AnimationEffectTiming/endDelay.html": [ "644eed9bf43bb0332ee33842ba0ad4423d90fc90", "testharness" ], "web-animations/interfaces/AnimationEffectTiming/fill.html": [ "bf5b77d3c96e737700e51f8a2c5b8e2b9629902f", @@ -203853,27 +203873,23 @@ "3a626b145f4eb77e816b9020f6fc67630088a00b", "testharness" ], "web-animations/interfaces/KeyframeEffect/composite.html": [ "2580086b2da9b29d1645484c5ad4e59636a370e5", "testharness" ], "web-animations/interfaces/KeyframeEffect/constructor.html": [ - "577241478fdeca6257711e9f90fec64372bd5637", + "1011b4146d1054ee6498cce1905230a10fdb9f96", "testharness" ], "web-animations/interfaces/KeyframeEffect/copy-contructor.html": [ "e1dfb5c05807a37974ecce98bb8c683cc291bfe4", "testharness" ], - "web-animations/interfaces/KeyframeEffect/effect-easing.html": [ - "1a8cf09dc40d3a53c0c7f17d6d7da81ab0b11b9e", - "testharness" - ], "web-animations/interfaces/KeyframeEffect/getComputedTiming.html": [ "c9dcf7c17010e5495007e000b33aeb4dc89f92b7", "testharness" ], "web-animations/interfaces/KeyframeEffect/iterationComposite.html": [ "5b7dbc28de885751ea952f4fecc2bd07cb3cea11", "testharness" ], @@ -203896,18 +203912,18 @@ "web-animations/interfaces/KeyframeEffectReadOnly/copy-contructor.html": [ "8ef986f13e7fe7ffeb7403f647b4169ac0d6a138", "testharness" ], "web-animations/interfaces/KeyframeEffectReadOnly/spacing.html": [ "ffca3a0ad5c7b08242224b80c52ebb8b9b7bfce6", "testharness" ], - "web-animations/resources/effect-easing-tests.js": [ - "429325e4ff9a4e1dc5277815a8f2a5ba2c9ebc3f", + "web-animations/resources/easing-tests.js": [ + "190134380a0724f470a03ed0aa20c936bfed8d6c", "support" ], "web-animations/resources/keyframe-utils.js": [ "7a0f21838f4bbda51fe7e0b5d8e55952c6cdcbd4", "support" ], "web-animations/testcommon.js": [ "001012b71248cdecba02215c827ab437b672e8c6", @@ -203944,16 +203960,20 @@ "web-animations/timing-model/animations/set-the-timeline-of-an-animation.html": [ "6e8e029f813046c3da69b4ff0c9d03d2a56b38a4", "testharness" ], "web-animations/timing-model/animations/updating-the-finished-state.html": [ "266f1b793aa74a59486081f3ba8f6cbb482e714b", "testharness" ], + "web-animations/timing-model/time-transformations/transformed-progress.html": [ + "6eebd47c9e60c9590de4d1747f8b8b7866a4a275", + "testharness" + ], "webaudio/.gitignore": [ "11bc81247643b0a9fc665f1e4b1f592cc1f4c670", "support" ], "webaudio/OWNERS": [ "d98264a830bdab63db07061e8b25080188e1aeab", "support" ],
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/009.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/009.html.ini @@ -1,5 +1,5 @@ [009.html] type: testharness [track CORS: No CORS, not same-origin, no headers] - expected: FAIL + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/010.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/010.html.ini @@ -1,5 +1,5 @@ [010.html] type: testharness [track CORS: Anonymous, not same-origin, no headers] - expected: FAIL + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/011.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/011.html.ini @@ -1,5 +1,5 @@ [011.html] type: testharness [track CORS: Anonymous, not same-origin, with headers] - expected: FAIL + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/012.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/012.html.ini @@ -1,5 +1,5 @@ [012.html] type: testharness [track CORS: Use Credentials, not same-origin, no headers] - expected: FAIL + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/013.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/013.html.ini @@ -1,5 +1,5 @@ [013.html] type: testharness [track CORS: Use Credentials, not same-origin, with headers] - expected: FAIL + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/014.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/014.html.ini @@ -1,6 +1,5 @@ [014.html] type: testharness - expected: TIMEOUT [track CORS: No CORS, same-origin, no headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/015.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/015.html.ini @@ -1,6 +1,5 @@ [015.html] type: testharness - expected: TIMEOUT [track CORS: No CORS, same-origin, with headers, redirects to same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/016.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/016.html.ini @@ -1,6 +1,5 @@ [016.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, no headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/017.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/017.html.ini @@ -1,6 +1,5 @@ [017.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, no headers, redirects to same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/018.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/018.html.ini @@ -1,6 +1,5 @@ [018.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, no headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/019.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/019.html.ini @@ -1,6 +1,5 @@ [019.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, with headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/020.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/020.html.ini @@ -1,6 +1,5 @@ [020.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, not same-origin, no headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/021.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/021.html.ini @@ -1,6 +1,5 @@ [021.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, not same-origin, with headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/022.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/022.html.ini @@ -1,6 +1,5 @@ [022.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, not same-origin, with headers, redirects to same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/023.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/023.html.ini @@ -1,6 +1,5 @@ [023.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, not same-origin, no headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/024.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/024.html.ini @@ -1,6 +1,5 @@ [024.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, not same-origin, with headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/025.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/025.html.ini @@ -1,6 +1,6 @@ [025.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, not same-origin, with headers, redirects to same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242 +
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/026.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/026.html.ini @@ -1,6 +1,6 @@ [026.html] type: testharness - expected: TIMEOUT [track CORS: No CORS, same-origin, with headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242 +
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/027.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/027.html.ini @@ -1,6 +1,5 @@ [027.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, no headers, redirects to not same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/028.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/028.html.ini @@ -1,6 +1,5 @@ [028.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, with headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/029.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/029.html.ini @@ -1,6 +1,5 @@ [029.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, no headers, redirects to not same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/030.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/030.html.ini @@ -1,6 +1,5 @@ [030.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, with headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/031.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/031.html.ini @@ -1,6 +1,5 @@ [031.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, not same-origin, no headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/032.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/032.html.ini @@ -1,6 +1,5 @@ [032.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, not same-origin, with headers, redirects to not same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/033.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/033.html.ini @@ -1,6 +1,5 @@ [033.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, not same-origin, with headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/034.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/034.html.ini @@ -1,6 +1,5 @@ [034.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, not same-origin, no headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/035.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/035.html.ini @@ -1,6 +1,5 @@ [035.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, not same-origin, with headers, redirects to not same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/036.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/036.html.ini @@ -1,6 +1,5 @@ [036.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, not same-origin, with headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/037.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/037.html.ini @@ -1,6 +1,5 @@ [037.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, no headers, redirects to not same-origin, no headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/038.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/038.html.ini @@ -1,6 +1,5 @@ [038.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, with headers, redirects to not same-origin, with headers, redirects to same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/039.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/039.html.ini @@ -1,6 +1,5 @@ [039.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, no headers, redirects to not same-origin, with headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/040.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/040.html.ini @@ -1,6 +1,5 @@ [040.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, no headers, redirects to not same-origin, no headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/041.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/041.html.ini @@ -1,6 +1,5 @@ [041.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, with headers, redirects to not same-origin, with headers, redirects to same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/042.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/042.html.ini @@ -1,6 +1,5 @@ [042.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, no headers, redirects to not same-origin, with headers, redirects to same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/043.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/043.html.ini @@ -1,6 +1,5 @@ [043.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/044.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/044.html.ini @@ -1,6 +1,5 @@ [044.html] type: testharness - expected: TIMEOUT [track CORS: Anonymous, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/045.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/045.html.ini @@ -1,6 +1,5 @@ [045.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, no headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/046.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cors/046.html.ini @@ -1,6 +1,5 @@ [046.html] type: testharness - expected: TIMEOUT [track CORS: Use Credentials, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, with headers] - expected: TIMEOUT + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1337242
rename from testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html rename to testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-overlapping-keyframes.html --- a/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html +++ b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-overlapping-keyframes.html @@ -1,11 +1,11 @@ <!DOCTYPE html> <meta charset=utf-8> -<title>Keyframe handling tests</title> +<title>Effect value computation tests when keyframes overlap</title> <link rel="help" href="https://w3c.github.io/web-animations/#the-effect-value-of-a-keyframe-animation-effect"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../testcommon.js"></script> <body> <div id="log"></div> <div id="target"></div> <script> @@ -64,51 +64,10 @@ test(function(t) { + ' overlap point should be used as interval startpoint'); anim.currentTime = 750; assert_equals(getComputedStyle(div).opacity, '0.85', 'After the overlap point, the last keyframe from the' + ' overlap point should be used as interval startpoint'); }, 'Overlapping keyframes between 0 and 1 use the appropriate value on each' + ' side of the overlap point'); -test(function(t) { - var div = createDiv(t); - var anim = div.animate({ visibility: ['hidden','visible'] }, - { duration: 100 * MS_PER_SEC, fill: 'both' }); - - anim.currentTime = 0; - assert_equals(getComputedStyle(div).visibility, 'hidden', - 'Visibility when progress = 0.'); - - anim.currentTime = 10 * MS_PER_SEC + 1; - assert_equals(getComputedStyle(div).visibility, 'visible', - 'Visibility when progress > 0 due to linear easing.'); - - anim.finish(); - assert_equals(getComputedStyle(div).visibility, 'visible', - 'Visibility when progress = 1.'); - -}, "Test visibility clamping behavior."); - -test(function(t) { - var div = createDiv(t); - var anim = div.animate({ visibility: ['hidden', 'visible'] }, - { duration: 100 * MS_PER_SEC, fill: 'both', - easing: 'cubic-bezier(0.25, -0.6, 0, 0.5)' }); - - anim.currentTime = 0; - assert_equals(getComputedStyle(div).visibility, 'hidden', - 'Visibility when progress = 0.'); - - // Timing function is below zero. So we expected visibility is hidden. - anim.currentTime = 10 * MS_PER_SEC + 1; - assert_equals(getComputedStyle(div).visibility, 'hidden', - 'Visibility when progress < 0 due to cubic-bezier easing.'); - - anim.currentTime = 60 * MS_PER_SEC; - assert_equals(getComputedStyle(div).visibility, 'visible', - 'Visibility when progress > 0 due to cubic-bezier easing.'); - -}, "Test visibility clamping behavior with an easing that has a negative component"); - -done(); </script> </body>
new file mode 100644 --- /dev/null +++ b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance.html @@ -0,0 +1,417 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Tests for calculation of the transformed distance when computing an effect value</title> +<link rel="help" href="https://w3c.github.io/web-animations/#the-effect-value-of-a-keyframe-animation-effect"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../../testcommon.js"></script> +<script src="../../resources/easing-tests.js"></script> +<body> +<div id="log"></div> +<div id="target"></div> +<script> +'use strict'; + +// Test that applying easing to keyframes is applied as expected + +gEasingTests.forEach(params => { + test(function(t) { + const target = createDiv(t); + const anim = target.animate([ { width: '0px' }, + // We put the easing on the second keyframe + // so we can test that it is only applied + // to the specified keyframe. + { width: '100px', easing: params.easing }, + { width: '200px' } ], + { duration: 2000, + fill: 'forwards' }); + + [ 0, 999, 1000, 1100, 1500, 2000 ].forEach(sampleTime => { + anim.currentTime = sampleTime; + + const portion = (sampleTime - 1000) / 1000; + const expectedWidth = sampleTime < 1000 + ? sampleTime / 10 // first segment is linear + : 100 + params.easingFunction(portion) * 100; + assert_approx_equals(parseFloat(getComputedStyle(target).width), + expectedWidth, + 0.01, + 'The width should be approximately ' + + `${expectedWidth} at ${sampleTime}ms`); + }); + }, `A ${params.desc} on a keyframe affects the resulting style`); +}); + +// Test that a linear-equivalent cubic-bezier easing applied to a keyframe does +// not alter (including clamping) the result. + +gEasingTests.forEach(params => { + const linearEquivalentEasings = [ 'cubic-bezier(0, 0, 0, 0)', + 'cubic-bezier(1, 1, 1, 1)' ]; + test(function(t) { + linearEquivalentEasings.forEach(linearEquivalentEasing => { + const timing = { duration: 1000, + fill: 'forwards', + easing: params.easing }; + + const linearTarget = createDiv(t); + const linearAnim = linearTarget.animate([ { width: '0px' }, + { width: '100px' } ], + timing); + + const equivalentTarget = createDiv(t); + const equivalentAnim = + equivalentTarget.animate([ { width: '0px', + easing: linearEquivalentEasing }, + { width: '100px' } ], + timing); + + [ 0, 250, 500, 750, 1000 ].forEach(sampleTime => { + linearAnim.currentTime = sampleTime; + equivalentAnim.currentTime = sampleTime; + + assert_equals(getComputedStyle(linearTarget).width, + getComputedStyle(equivalentTarget).width, + `The 'width' of the animated elements should be equal ` + + `at ${sampleTime}ms`); + }); + }); + }, 'Linear-equivalent cubic-bezier keyframe easing applied to an effect ' + + `with a ${params.desc} does not alter the result`); +}); + +// Test that different easing functions correctly handle inputs outside the +// range [0, 1]. This only occurs when we have an easing specified on the +// effect that produces a value outside [0, 1] which we then pass to an easing +// on a keyframe. + +function assert_style_left_at(animation, time, easingFunction) { + animation.currentTime = time; + var portion = time / animation.effect.timing.duration; + assert_approx_equals(pxToNum(getComputedStyle(animation.effect.target).left), + easingFunction(portion) * 100, + 0.01, + 'The left of the animation should be approximately ' + + easingFunction(portion) * 100 + ' at ' + time + 'ms'); +} + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate([ { left: '0px', easing: 'step-start' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); + + // The bezier function produces values greater than 1 (but always less than 2) + // in (0.23368794, 1) + anim.currentTime = 0; + assert_equals(getComputedStyle(target).left, '100px'); + anim.currentTime = 230; + assert_equals(getComputedStyle(target).left, '100px'); + anim.currentTime = 250; + assert_equals(getComputedStyle(target).left, '200px'); + anim.currentTime = 1000; + assert_equals(getComputedStyle(target).left, '100px'); +}, 'step-start easing with input progress greater than 1'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate([ { left: '0px', easing: 'step-end' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); + + // The bezier function produces values greater than 1 (but always less than 2) + // in (0.23368794, 1) + anim.currentTime = 0; + assert_equals(getComputedStyle(target).left, '0px'); + anim.currentTime = 230; + assert_equals(getComputedStyle(target).left, '0px'); + anim.currentTime = 250; + assert_equals(getComputedStyle(target).left, '100px'); + anim.currentTime = 1000; + assert_equals(getComputedStyle(target).left, '100px'); +}, 'step-end easing with input progress greater than 1'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate([ { left: '0px', easing: 'step-end' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, 3, 1, 3)' }); + + // The bezier function produces values greater than 2 (but always less than 3) + // in the range (~0.245, ~0.882) + anim.currentTime = 0; + assert_equals(getComputedStyle(target).left, '0px'); + anim.currentTime = 500; + assert_equals(getComputedStyle(target).left, '200px'); + anim.currentTime = 900; + assert_equals(getComputedStyle(target).left, '100px'); +}, 'step-end easing with input progress greater than 2'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate([ { left: '0px', easing: 'step-start' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); + + // The bezier function produces negative values (but always greater than -1) + // in (0, 0.766312060) + anim.currentTime = 0; + assert_equals(getComputedStyle(target).left, '100px'); + anim.currentTime = 750; + assert_equals(getComputedStyle(target).left, '0px'); + anim.currentTime = 800; + assert_equals(getComputedStyle(target).left, '100px'); + anim.currentTime = 1000; + assert_equals(getComputedStyle(target).left, '100px'); +}, 'step-start easing with input progress less than 0'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate([ { left: '0px', easing: 'step-start' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, -2, 1, -2)' }); + + // The bezier function produces values less than -1 (but always greater than + // -2) in the range (~0.118, ~0.755) + anim.currentTime = 0; + assert_equals(getComputedStyle(target).left, '100px'); + anim.currentTime = 100; + assert_equals(getComputedStyle(target).left, '0px'); + anim.currentTime = 500; + assert_equals(getComputedStyle(target).left, '-100px'); + anim.currentTime = 1000; + assert_equals(getComputedStyle(target).left, '100px'); +}, 'step-start easing with input progress less than -1'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate([ { left: '0px', easing: 'step-end' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); + + // The bezier function produces negative values (but always greater than -1) + // in (0, 0.766312060) + anim.currentTime = 0; + assert_equals(getComputedStyle(target).left, '0px'); + anim.currentTime = 750; + assert_equals(getComputedStyle(target).left, '-100px'); + anim.currentTime = 800; + assert_equals(getComputedStyle(target).left, '0px'); + anim.currentTime = 1000; + assert_equals(getComputedStyle(target).left, '100px'); +}, 'step-end easing with input progress less than 0'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate( + // http://cubic-bezier.com/#.5,1,.5,0 + [ { left: '0px', easing: 'cubic-bezier(0.5, 1, 0.5, 0)' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); + var keyframeEasing = function(x) { + assert_greater_than_equal(x, 0.0, + 'This function should be called in [0, 1.0] range'); + assert_less_than_equal(x, 1.0, + 'This function should be called in [0, 1.0] range'); + return cubicBezier(0.5, 1, 0.5, 0)(x); + } + var keyframeEasingExtrapolated = function(x) { + assert_greater_than(x, 1.0, + 'This function should be called in (1.0, infinity) range'); + // p3x + (p2y - p3y) / (p2x - p3x) * (x - p3x) + return 1.0 + (0 - 1) / (0.5 - 1) * (x - 1.0); + } + var effectEasing = function(x) { + return cubicBezier(0, 1.5, 1, 1.5)(x); + } + + // The effect-easing produces values greater than 1 in (0.23368794, 1) + assert_style_left_at(anim, 0, function(x) { + return keyframeEasing(effectEasing(x)); + }); + assert_style_left_at(anim, 230, function(x) { + return keyframeEasing(effectEasing(x)); + }); + assert_style_left_at(anim, 240, function(x) { + return keyframeEasingExtrapolated(effectEasing(x)); + }); + // Near the extreme point of the effect-easing function + assert_style_left_at(anim, 700, function(x) { + return keyframeEasingExtrapolated(effectEasing(x)); + }); + assert_style_left_at(anim, 990, function(x) { + return keyframeEasingExtrapolated(effectEasing(x)); + }); + assert_style_left_at(anim, 1000, function(x) { + return keyframeEasing(effectEasing(x)); + }); +}, 'cubic-bezier easing with input progress greater than 1'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate( + // http://cubic-bezier.com/#0,1.5,1,1.5 + [ { left: '0px', easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); + var easing = function(x) { + assert_greater_than_equal(x, 0.0, + 'This function should be called in [0, 1.0] range'); + assert_less_than_equal(x, 1.0, + 'This function should be called in [0, 1.0] range'); + return cubicBezier(0, 1.5, 1, 1.5)(x); + } + var easingExtrapolated = function(x) { + assert_greater_than(x, 1.0, + 'This function should be called in negative range'); + // For cubic-bezier(0, 1.5, 1, 1.5), the tangent at the + // endpoint (x = 1.0) is infinity so we should just return 1.0. + return 1.0; + } + + // The effect-easing produces values greater than 1 in (0.23368794, 1) + assert_style_left_at(anim, 0, function(x) { + return easing(easing(x)) + }); + assert_style_left_at(anim, 230, function(x) { + return easing(easing(x)) + }); + assert_style_left_at(anim, 240, function(x) { + return easingExtrapolated(easing(x)); + }); + // Near the extreme point of the effect-easing function + assert_style_left_at(anim, 700, function(x) { + return easingExtrapolated(easing(x)); + }); + assert_style_left_at(anim, 990, function(x) { + return easingExtrapolated(easing(x)); + }); + assert_style_left_at(anim, 1000, function(x) { + return easing(easing(x)) + }); +}, 'cubic-bezier easing with input progress greater than 1 and where the ' + + 'tangent on the upper boundary is infinity'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate( + // http://cubic-bezier.com/#.5,1,.5,0 + [ { left: '0px', easing: 'cubic-bezier(0.5, 1, 0.5, 0)' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); + var keyframeEasing = function(x) { + assert_greater_than_equal(x, 0.0, + 'This function should be called in [0, 1.0] range'); + assert_less_than_equal(x, 1.0, + 'This function should be called in [0, 1.0] range'); + return cubicBezier(0.5, 1, 0.5, 0)(x); + } + var keyframeEasingExtrapolated = function(x) { + assert_less_than(x, 0.0, + 'This function should be called in negative range'); + // p0x + (p1y - p0y) / (p1x - p0x) * (x - p0x) + return (1 / 0.5) * x; + } + var effectEasing = function(x) { + return cubicBezier(0, -0.5, 1, -0.5)(x); + } + + // The effect-easing produces negative values in (0, 0.766312060) + assert_style_left_at(anim, 0, function(x) { + return keyframeEasing(effectEasing(x)); + }); + assert_style_left_at(anim, 10, function(x) { + return keyframeEasingExtrapolated(effectEasing(x)); + }); + // Near the extreme point of the effect-easing function + assert_style_left_at(anim, 300, function(x) { + return keyframeEasingExtrapolated(effectEasing(x)); + }); + assert_style_left_at(anim, 750, function(x) { + return keyframeEasingExtrapolated(effectEasing(x)); + }); + assert_style_left_at(anim, 770, function(x) { + return keyframeEasing(effectEasing(x)); + }); + assert_style_left_at(anim, 1000, function(x) { + return keyframeEasing(effectEasing(x)); + }); +}, 'cubic-bezier easing with input progress less than 0'); + +test(function(t) { + var target = createDiv(t); + target.style.position = 'absolute'; + var anim = target.animate( + // http://cubic-bezier.com/#0,-0.5,1,-0.5 + [ { left: '0px', easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }, + { left: '100px' } ], + { duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); + var easing = function(x) { + assert_greater_than_equal(x, 0.0, + 'This function should be called in [0, 1.0] range'); + assert_less_than_equal(x, 1.0, + 'This function should be called in [0, 1.0] range'); + return cubicBezier(0, -0.5, 1, -0.5)(x); + } + var easingExtrapolated = function(x) { + assert_less_than(x, 0.0, + 'This function should be called in negative range'); + // For cubic-bezier(0, -0.5, 1, -0.5), the tangent at the + // endpoint (x = 0.0) is infinity so we should just return 0.0. + return 0.0; + } + + // The effect-easing produces negative values in (0, 0.766312060) + assert_style_left_at(anim, 0, function(x) { + return easing(easing(x)) + }); + assert_style_left_at(anim, 10, function(x) { + return easingExtrapolated(easing(x)); + }); + // Near the extreme point of the effect-easing function + assert_style_left_at(anim, 300, function(x) { + return easingExtrapolated(easing(x)); + }); + assert_style_left_at(anim, 750, function(x) { + return easingExtrapolated(easing(x)); + }); + assert_style_left_at(anim, 770, function(x) { + return easing(easing(x)) + }); + assert_style_left_at(anim, 1000, function(x) { + return easing(easing(x)) + }); +}, 'cubic-bezier easing with input progress less than 0 and where the ' + + 'tangent on the lower boundary is infinity'); + +</script> +</body>
copy from testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html copy to testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-visibility.html --- a/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html +++ b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-visibility.html @@ -1,114 +1,55 @@ <!DOCTYPE html> <meta charset=utf-8> -<title>Keyframe handling tests</title> +<title>Effect value computation tests for 'visibility' property</title> <link rel="help" href="https://w3c.github.io/web-animations/#the-effect-value-of-a-keyframe-animation-effect"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../testcommon.js"></script> <body> <div id="log"></div> <div id="target"></div> <script> 'use strict'; test(function(t) { var div = createDiv(t); - var anim = div.animate([ { offset: 0, opacity: 0 }, - { offset: 0, opacity: 0.1 }, - { offset: 0, opacity: 0.2 }, - { offset: 1, opacity: 0.8 }, - { offset: 1, opacity: 0.9 }, - { offset: 1, opacity: 1 } ], - { duration: 1000, - easing: 'cubic-bezier(0.5, -0.5, 0.5, 1.5)' }); - assert_equals(getComputedStyle(div).opacity, '0.2', - 'When progress is zero the last keyframe with offset 0 should' - + ' be used'); - // http://cubic-bezier.com/#.5,-0.5,.5,1.5 - // At t=0.15, the progress should be negative - anim.currentTime = 150; - assert_equals(getComputedStyle(div).opacity, '0', - 'When progress is negative, the first keyframe with a 0 offset' - + ' should be used'); - // At t=0.71, the progress should be just less than 1 - anim.currentTime = 710; - assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.8, 0.01, - 'When progress is just less than 1, the first keyframe with an' - + ' offset of 1 should be used as the interval endpoint'); - // At t=0.85, the progress should be > 1 - anim.currentTime = 850; - assert_equals(getComputedStyle(div).opacity, '1', - 'When progress is greater than 1.0, the last keyframe with a 1' - + ' offset should be used'); - anim.finish(); - assert_equals(getComputedStyle(div).opacity, '1', - 'When progress is equal to 1.0, the last keyframe with a 1' - + ' offset should be used'); -}, 'Overlapping keyframes at 0 and 1 use the appropriate value when the' - + ' progress is outside the range [0, 1]'); - -test(function(t) { - var div = createDiv(t); - var anim = div.animate([ { offset: 0, opacity: 0 }, - { offset: 0.5, opacity: 0.3 }, - { offset: 0.5, opacity: 0.5 }, - { offset: 0.5, opacity: 0.7 }, - { offset: 1, opacity: 1 } ], 1000); - anim.currentTime = 250; - assert_equals(getComputedStyle(div).opacity, '0.15', - 'Before the overlap point, the first keyframe from the' - + ' overlap point should be used as interval endpoint'); - anim.currentTime = 500; - assert_equals(getComputedStyle(div).opacity, '0.7', - 'At the overlap point, the last keyframe from the' - + ' overlap point should be used as interval startpoint'); - anim.currentTime = 750; - assert_equals(getComputedStyle(div).opacity, '0.85', - 'After the overlap point, the last keyframe from the' - + ' overlap point should be used as interval startpoint'); -}, 'Overlapping keyframes between 0 and 1 use the appropriate value on each' - + ' side of the overlap point'); - -test(function(t) { - var div = createDiv(t); var anim = div.animate({ visibility: ['hidden','visible'] }, { duration: 100 * MS_PER_SEC, fill: 'both' }); anim.currentTime = 0; assert_equals(getComputedStyle(div).visibility, 'hidden', - 'Visibility when progress = 0.'); + 'Visibility when progress = 0'); anim.currentTime = 10 * MS_PER_SEC + 1; assert_equals(getComputedStyle(div).visibility, 'visible', - 'Visibility when progress > 0 due to linear easing.'); + 'Visibility when progress > 0 due to linear easing'); anim.finish(); assert_equals(getComputedStyle(div).visibility, 'visible', - 'Visibility when progress = 1.'); + 'Visibility when progress = 1'); -}, "Test visibility clamping behavior."); +}, 'Visibility clamping behavior'); test(function(t) { var div = createDiv(t); var anim = div.animate({ visibility: ['hidden', 'visible'] }, { duration: 100 * MS_PER_SEC, fill: 'both', easing: 'cubic-bezier(0.25, -0.6, 0, 0.5)' }); anim.currentTime = 0; assert_equals(getComputedStyle(div).visibility, 'hidden', - 'Visibility when progress = 0.'); + 'Visibility when progress = 0'); // Timing function is below zero. So we expected visibility is hidden. anim.currentTime = 10 * MS_PER_SEC + 1; assert_equals(getComputedStyle(div).visibility, 'hidden', - 'Visibility when progress < 0 due to cubic-bezier easing.'); + 'Visibility when progress < 0 due to cubic-bezier easing'); anim.currentTime = 60 * MS_PER_SEC; assert_equals(getComputedStyle(div).visibility, 'visible', - 'Visibility when progress > 0 due to cubic-bezier easing.'); + 'Visibility when progress > 0 due to cubic-bezier easing'); -}, "Test visibility clamping behavior with an easing that has a negative component"); +}, 'Visibility clamping behavior with an easing that has a negative component'); -done(); </script> </body>
--- a/testing/web-platform/tests/web-animations/interfaces/Animatable/animate.html +++ b/testing/web-platform/tests/web-animations/interfaces/Animatable/animate.html @@ -1,15 +1,16 @@ <!DOCTYPE html> <meta charset=utf-8> <title>Animatable.animate tests</title> <link rel="help" href="https://w3c.github.io/web-animations/#dom-animatable-animate"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../testcommon.js"></script> +<script src="../../resources/easing-tests.js"></script> <script src="../../resources/keyframe-utils.js"></script> <body> <div id="log"></div> <iframe width="10" height="10" id="iframe"></iframe> <script> 'use strict'; // Tests on Element @@ -91,16 +92,25 @@ gInvalidKeyframesTests.forEach(function( test(function(t) { var div = createDiv(t); assert_throws(subtest.expected, function() { div.animate(subtest.input, 2000); }); }, 'Element.animate() does not accept ' + subtest.desc); }); +gInvalidEasings.forEach(invalidEasing => { + test(function(t) { + var div = createDiv(t); + assert_throws(new TypeError, () => { + div.animate({ easing: invalidEasing }, 2000); + }); + }, `Element.animate() does not accept invalid easing: '${invalidEasing}'`); +}); + test(function(t) { var div = createDiv(t); var anim = div.animate({ opacity: [ 0, 1 ] }, 2000); assert_equals(anim.effect.timing.duration, 2000); // Also check that unspecified parameters receive their default values assert_equals(anim.effect.timing.fill, 'auto'); }, 'Element.animate() accepts a double as an options argument');
--- a/testing/web-platform/tests/web-animations/interfaces/AnimationEffectTiming/easing.html +++ b/testing/web-platform/tests/web-animations/interfaces/AnimationEffectTiming/easing.html @@ -1,32 +1,32 @@ <!DOCTYPE html> <meta charset=utf-8> <title>easing tests</title> <link rel="help" href="https://w3c.github.io/web-animations/#dom-animationeffecttiming-easing"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../testcommon.js"></script> -<script src="../../resources/effect-easing-tests.js"></script> +<script src="../../resources/easing-tests.js"></script> <body> <div id="log"></div> <script> 'use strict'; function assert_progress(animation, currentTime, easingFunction) { animation.currentTime = currentTime; var portion = currentTime / animation.effect.timing.duration; assert_approx_equals(animation.effect.getComputedTiming().progress, easingFunction(portion), 0.01, 'The progress of the animation should be approximately ' + easingFunction(portion) + ' at ' + currentTime + 'ms'); } -gEffectEasingTests.forEach(function(options) { +gEasingTests.forEach(function(options) { test(function(t) { var target = createDiv(t); var anim = target.animate([ { opacity: 0 }, { opacity: 1 } ], { duration: 1000 * MS_PER_SEC, fill: 'forwards' }); anim.effect.timing.easing = options.easing; assert_equals(anim.effect.timing.easing, options.serialization || options.easing); @@ -35,25 +35,25 @@ gEffectEasingTests.forEach(function(opti assert_progress(anim, 0, easing); assert_progress(anim, 250 * MS_PER_SEC, easing); assert_progress(anim, 500 * MS_PER_SEC, easing); assert_progress(anim, 750 * MS_PER_SEC, easing); assert_progress(anim, 1000 * MS_PER_SEC, easing); }, options.desc); }); -gInvalidEasingTests.forEach(function(options) { +gInvalidEasings.forEach(function(invalidEasing) { test(function(t) { var div = createDiv(t); var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC); assert_throws({ name: 'TypeError' }, function() { - anim.effect.timing.easing = options.easing; + anim.effect.timing.easing = invalidEasing; }); - }, 'Invalid effect easing value test: \'' + options.easing + '\''); + }, 'Invalid effect easing value test: \'' + invalidEasing + '\''); }); test(function(t) { var delay = 1000 * MS_PER_SEC; var target = createDiv(t); var anim = target.animate([ { opacity: 0 }, { opacity: 1 } ], { duration: 1000 * MS_PER_SEC,
--- a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/constructor.html +++ b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/constructor.html @@ -1,15 +1,16 @@ <!DOCTYPE html> <meta charset=utf-8> <title>KeyframeEffectReadOnly constructor tests</title> <link rel="help" href="https://w3c.github.io/web-animations/#the-keyframeeffect-interfaces"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../testcommon.js"></script> +<script src="../../resources/easing-tests.js"></script> <script src="../../resources/keyframe-utils.js"></script> <body> <div id="log"></div> <div id="target"></div> <style> #target { border-style: solid; /* so border-*-width values don't compute to 0 */ } @@ -61,17 +62,35 @@ test(function(t) { var expected = subtest[1]; var effect = new KeyframeEffectReadOnly(target, { left: ["10px", "20px"] }, { easing: easing }); assert_equals(effect.timing.easing, expected, "resulting easing for '" + easing + "'"); }); }, "easing values are parsed correctly when passed to the " + - "KeyframeEffectReadOnly constructor in KeyframeTimingOptions"); + "KeyframeEffectReadOnly constructor in KeyframeEffectOptions"); + +test(function(t) { + gInvalidEasings.forEach(invalidEasing => { + assert_throws(new TypeError, () => { + new KeyframeEffectReadOnly(target, { easing: invalidEasing }); + }, `TypeError is thrown for easing '${invalidEasing}'`); + }); +}, 'invalid easing values are correctly rejected when passed to the ' + + 'KeyframeEffectReadOnly constructor in regular keyframes'); + +test(function(t) { + gInvalidEasings.forEach(invalidEasing => { + assert_throws(new TypeError, () => { + new KeyframeEffectReadOnly(target, null, { easing: invalidEasing }); + }, `TypeError is thrown for easing '${invalidEasing}'`); + }); +}, 'invalid easing values are correctly rejected when passed to the ' + + 'KeyframeEffectReadOnly constructor in KeyframeEffectOptions'); test(function(t) { var getKeyframe = function(composite) { return { left: [ "10px", "20px" ], composite: composite }; }; gGoodKeyframeCompositeValueTests.forEach(function(composite) { var effect = new KeyframeEffectReadOnly(target, getKeyframe(composite)); assert_equals(effect.getKeyframes()[0].composite, composite,
deleted file mode 100644 --- a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/effect-easing.html +++ /dev/null @@ -1,683 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<title>Effect-level easing tests</title> -<link rel="help" href="http://w3c.github.io/web-animations/#calculating-the-transformed-time"> -<link rel="author" title="Hiroyuki Ikezoe" href="mailto:hiikezoe@mozilla-japan.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="../../testcommon.js"></script> -<script src="../../resources/effect-easing-tests.js"></script> -<body> -<div id="log"></div> -<div id="target"></div> -<script> -"use strict"; - -function assert_style_left_at(animation, time, easingFunction) { - animation.currentTime = time; - var portion = time / animation.effect.timing.duration; - assert_approx_equals(pxToNum(getComputedStyle(animation.effect.target).left), - easingFunction(portion) * 100, - 0.01, - 'The left of the animation should be approximately ' + - easingFunction(portion) * 100 + ' at ' + time + 'ms'); -} - -gEffectEasingTests.forEach(function(options) { - test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate([ { left: '0px' }, { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: options.easing }); - var easing = options.easingFunction; - - anim.pause(); - - assert_style_left_at(anim, 0, easing); - assert_style_left_at(anim, 250, easing); - assert_style_left_at(anim, 500, easing); - assert_style_left_at(anim, 750, easing); - assert_style_left_at(anim, 1000, easing); - }, options.desc); -}); - -var gEffectEasingTestsWithKeyframeEasing = [ - { - desc: 'effect easing produces values greater than 1 with keyframe ' + - 'easing cubic-bezier(0, 0, 0, 0)', - easing: 'cubic-bezier(0, 1.5, 1, 1.5)', - keyframeEasing: 'cubic-bezier(0, 0, 0, 0)', // linear - easingFunction: cubicBezier(0, 1.5, 1, 1.5) - }, - { - desc: 'effect easing produces values greater than 1 with keyframe ' + - 'easing cubic-bezier(1, 1, 1, 1)', - easing: 'cubic-bezier(0, 1.5, 1, 1.5)', - keyframeEasing: 'cubic-bezier(1, 1, 1, 1)', // linear - easingFunction: cubicBezier(0, 1.5, 1, 1.5) - }, - { - desc: 'effect easing produces negative values 1 with keyframe ' + - 'easing cubic-bezier(0, 0, 0, 0)', - easing: 'cubic-bezier(0, -0.5, 1, -0.5)', - keyframeEasing: 'cubic-bezier(0, 0, 0, 0)', // linear - easingFunction: cubicBezier(0, -0.5, 1, -0.5) - }, - { - desc: 'effect easing produces negative values 1 with keyframe ' + - 'easing cubic-bezier(1, 1, 1, 1)', - easing: 'cubic-bezier(0, -0.5, 1, -0.5)', - keyframeEasing: 'cubic-bezier(1, 1, 1, 1)', // linear - easingFunction: cubicBezier(0, -0.5, 1, -0.5) - }, -]; - -gEffectEasingTestsWithKeyframeEasing.forEach(function(options) { - test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate( - [ { left: '0px', easing: options.keyframeEasing }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: options.easing }); - var easing = options.easingFunction; - - anim.pause(); - - assert_style_left_at(anim, 0, easing); - assert_style_left_at(anim, 250, easing); - assert_style_left_at(anim, 500, easing); - assert_style_left_at(anim, 750, easing); - assert_style_left_at(anim, 1000, easing); - }, options.desc); -}); - -// Other test cases that effect easing produces values outside of [0,1]. -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate([ { left: '0px', easing: 'step-start' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); - anim.pause(); - - // The bezier function produces values greater than 1 in (0.23368794, 1) - anim.currentTime = 0; - assert_equals(getComputedStyle(target).left, '100px'); - anim.currentTime = 230; - assert_equals(getComputedStyle(target).left, '100px'); - anim.currentTime = 250; - assert_equals(getComputedStyle(target).left, '100px'); - anim.currentTime = 1000; - assert_equals(getComputedStyle(target).left, '100px'); -}, 'effect easing produces values greater than 1 with step-start keyframe'); - -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate([ { left: '0px', easing: 'step-end' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); - anim.pause(); - - // The bezier function produces values greater than 1 in (0.23368794, 1) - anim.currentTime = 0; - assert_equals(getComputedStyle(target).left, '0px'); - anim.currentTime = 230; - assert_equals(getComputedStyle(target).left, '0px'); - anim.currentTime = 250; - assert_equals(getComputedStyle(target).left, '100px'); - anim.currentTime = 1000; - assert_equals(getComputedStyle(target).left, '100px'); -}, 'effect easing produces values greater than 1 with step-end keyframe'); - -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate([ { left: '0px', easing: 'step-start' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); - anim.pause(); - - // The bezier function produces negative values in (0, 0.766312060) - anim.currentTime = 0; - assert_equals(getComputedStyle(target).left, '100px'); - anim.currentTime = 750; - assert_equals(getComputedStyle(target).left, '0px'); - anim.currentTime = 800; - assert_equals(getComputedStyle(target).left, '100px'); - anim.currentTime = 1000; - assert_equals(getComputedStyle(target).left, '100px'); -}, 'effect easing produces negative values with step-start keyframe'); - -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate([ { left: '0px', easing: 'step-end' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); - anim.pause(); - - // The bezier function produces negative values in (0, 0.766312060) - anim.currentTime = 0; - assert_equals(getComputedStyle(target).left, '0px'); - anim.currentTime = 750; - assert_equals(getComputedStyle(target).left, '0px'); - anim.currentTime = 800; - assert_equals(getComputedStyle(target).left, '0px'); - anim.currentTime = 1000; - assert_equals(getComputedStyle(target).left, '100px'); -}, 'effect easing produces negative values with step-end keyframe'); - -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate( - // http://cubic-bezier.com/#.5,1,.5,0 - [ { left: '0px', easing: 'cubic-bezier(0.5, 1, 0.5, 0)' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); - var keyframeEasing = function(x) { - assert_greater_than_equal(x, 0.0, - 'This function should be called in [0, 1.0] range'); - assert_less_than_equal(x, 1.0, - 'This function should be called in [0, 1.0] range'); - return cubicBezier(0.5, 1, 0.5, 0)(x); - } - var keyframeEasingExtrapolated = function(x) { - assert_greater_than(x, 1.0, - 'This function should be called in (1.0, infinity) range'); - // p3x + (p2y - p3y) / (p2x - p3x) * (x - p3x) - return 1.0 + (0 - 1) / (0.5 - 1) * (x - 1.0); - } - var effectEasing = function(x) { - return cubicBezier(0, 1.5, 1, 1.5)(x); - } - - anim.pause(); - - // The effect-easing produces values greater than 1 in (0.23368794, 1) - assert_style_left_at(anim, 0, function(x) { - return keyframeEasing(effectEasing(x)); - }); - assert_style_left_at(anim, 230, function(x) { - return keyframeEasing(effectEasing(x)); - }); - assert_style_left_at(anim, 240, function(x) { - return keyframeEasingExtrapolated(effectEasing(x)); - }); - // Near the extreme point of the effect-easing function - assert_style_left_at(anim, 700, function(x) { - return keyframeEasingExtrapolated(effectEasing(x)); - }); - assert_style_left_at(anim, 990, function(x) { - return keyframeEasingExtrapolated(effectEasing(x)); - }); - assert_style_left_at(anim, 1000, function(x) { - return keyframeEasing(effectEasing(x)); - }); -}, 'effect easing produces values greater than 1 with keyframe easing ' + - 'producing values greater than 1'); - -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate( - // http://cubic-bezier.com/#0,1.5,1,1.5 - [ { left: '0px', easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, 1.5, 1, 1.5)' }); - var easing = function(x) { - assert_greater_than_equal(x, 0.0, - 'This function should be called in [0, 1.0] range'); - assert_less_than_equal(x, 1.0, - 'This function should be called in [0, 1.0] range'); - return cubicBezier(0, 1.5, 1, 1.5)(x); - } - var easingExtrapolated = function(x) { - assert_greater_than(x, 1.0, - 'This function should be called in negative range'); - // For cubic-bezier(0, 1.5, 1, 1.5), the tangent at the - // endpoint (x = 1.0) is infinity so we should just return 1.0. - return 1.0; - } - - anim.pause(); - - // The effect-easing produces values greater than 1 in (0.23368794, 1) - assert_style_left_at(anim, 0, function(x) { - return easing(easing(x)) - }); - assert_style_left_at(anim, 230, function(x) { - return easing(easing(x)) - }); - assert_style_left_at(anim, 240, function(x) { - return easingExtrapolated(easing(x)); - }); - // Near the extreme point of the effect-easing function - assert_style_left_at(anim, 700, function(x) { - return easingExtrapolated(easing(x)); - }); - assert_style_left_at(anim, 990, function(x) { - return easingExtrapolated(easing(x)); - }); - assert_style_left_at(anim, 1000, function(x) { - return easing(easing(x)) - }); -}, 'effect easing which produces values greater than 1 and the tangent on ' + - 'the upper boundary is infinity with keyframe easing producing values ' + - 'greater than 1'); - -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate( - // http://cubic-bezier.com/#.5,1,.5,0 - [ { left: '0px', easing: 'cubic-bezier(0.5, 1, 0.5, 0)' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); - var keyframeEasing = function(x) { - assert_greater_than_equal(x, 0.0, - 'This function should be called in [0, 1.0] range'); - assert_less_than_equal(x, 1.0, - 'This function should be called in [0, 1.0] range'); - return cubicBezier(0.5, 1, 0.5, 0)(x); - } - var keyframeEasingExtrapolated = function(x) { - assert_less_than(x, 0.0, - 'This function should be called in negative range'); - // p0x + (p1y - p0y) / (p1x - p0x) * (x - p0x) - return (1 / 0.5) * x; - } - var effectEasing = function(x) { - return cubicBezier(0, -0.5, 1, -0.5)(x); - } - - anim.pause(); - - // The effect-easing produces negative values in (0, 0.766312060) - assert_style_left_at(anim, 0, function(x) { - return keyframeEasing(effectEasing(x)); - }); - assert_style_left_at(anim, 10, function(x) { - return keyframeEasingExtrapolated(effectEasing(x)); - }); - // Near the extreme point of the effect-easing function - assert_style_left_at(anim, 300, function(x) { - return keyframeEasingExtrapolated(effectEasing(x)); - }); - assert_style_left_at(anim, 750, function(x) { - return keyframeEasingExtrapolated(effectEasing(x)); - }); - assert_style_left_at(anim, 770, function(x) { - return keyframeEasing(effectEasing(x)); - }); - assert_style_left_at(anim, 1000, function(x) { - return keyframeEasing(effectEasing(x)); - }); -}, 'effect easing produces negative values with keyframe easing ' + - 'producing negative values'); - -test(function(t) { - var target = createDiv(t); - target.style.position = 'absolute'; - var anim = target.animate( - // http://cubic-bezier.com/#0,-0.5,1,-0.5 - [ { left: '0px', easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }, - { left: '100px' } ], - { duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0, -0.5, 1, -0.5)' }); - var easing = function(x) { - assert_greater_than_equal(x, 0.0, - 'This function should be called in [0, 1.0] range'); - assert_less_than_equal(x, 1.0, - 'This function should be called in [0, 1.0] range'); - return cubicBezier(0, -0.5, 1, -0.5)(x); - } - var easingExtrapolated = function(x) { - assert_less_than(x, 0.0, - 'This function should be called in negative range'); - // For cubic-bezier(0, -0.5, 1, -0.5), the tangent at the - // endpoint (x = 0.0) is infinity so we should just return 0.0. - return 0.0; - } - - anim.pause(); - - // The effect-easing produces negative values in (0, 0.766312060) - assert_style_left_at(anim, 0, function(x) { - return easing(easing(x)) - }); - assert_style_left_at(anim, 10, function(x) { - return easingExtrapolated(easing(x)); - }); - // Near the extreme point of the effect-easing function - assert_style_left_at(anim, 300, function(x) { - return easingExtrapolated(easing(x)); - }); - assert_style_left_at(anim, 750, function(x) { - return easingExtrapolated(easing(x)); - }); - assert_style_left_at(anim, 770, function(x) { - return easing(easing(x)) - }); - assert_style_left_at(anim, 1000, function(x) { - return easing(easing(x)) - }); -}, 'effect easing which produces negative values and the tangent on ' + - 'the lower boundary is infinity with keyframe easing producing ' + - 'negative values'); - -var gStepTimingFunctionTests = [ - { - description: 'Test bounds point of step-start easing', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 0 }, - { currentTime: 999, progress: 0 }, - { currentTime: 1000, progress: 0.5 }, - { currentTime: 1499, progress: 0.5 }, - { currentTime: 1500, progress: 1 }, - { currentTime: 2000, progress: 1 } - ] - }, - { - description: 'Test bounds point of step-start easing with compositor', - keyframe: [ { opacity: 0 }, - { opacity: 1 } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 0 }, - { currentTime: 999, progress: 0 }, - { currentTime: 1000, progress: 0.5 }, - { currentTime: 1499, progress: 0.5 }, - { currentTime: 1500, progress: 1 }, - { currentTime: 2000, progress: 1 } - ] - }, - { - description: 'Test bounds point of step-start easing with reverse direction', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - direction: 'reverse', - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 1 }, - { currentTime: 1001, progress: 1 }, - { currentTime: 1500, progress: 1 }, - { currentTime: 1501, progress: 0.5 }, - { currentTime: 2000, progress: 0 }, - { currentTime: 2500, progress: 0 } - ] - }, - { - description: 'Test bounds point of step-start easing ' + - 'with iterationStart not at a transition point', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - iterationStart: 0.25, - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 0.5 }, - { currentTime: 999, progress: 0.5 }, - { currentTime: 1000, progress: 0.5 }, - { currentTime: 1249, progress: 0.5 }, - { currentTime: 1250, progress: 1 }, - { currentTime: 1749, progress: 1 }, - { currentTime: 1750, progress: 0.5 }, - { currentTime: 2000, progress: 0.5 }, - { currentTime: 2500, progress: 0.5 }, - ] - }, - { - description: 'Test bounds point of step-start easing ' + - 'with iterationStart and delay', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - iterationStart: 0.5, - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 0.5 }, - { currentTime: 999, progress: 0.5 }, - { currentTime: 1000, progress: 1 }, - { currentTime: 1499, progress: 1 }, - { currentTime: 1500, progress: 0.5 }, - { currentTime: 2000, progress: 1 } - ] - }, - { - description: 'Test bounds point of step-start easing ' + - 'with iterationStart and reverse direction', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - iterationStart: 0.5, - direction: 'reverse', - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 1 }, - { currentTime: 1000, progress: 1 }, - { currentTime: 1001, progress: 0.5 }, - { currentTime: 1499, progress: 0.5 }, - { currentTime: 1500, progress: 1 }, - { currentTime: 1999, progress: 1 }, - { currentTime: 2000, progress: 0.5 }, - { currentTime: 2500, progress: 0.5 } - ] - }, - { - description: 'Test bounds point of step(4, start) easing ' + - 'with iterationStart 0.75 and delay', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - duration: 1000, - fill: 'both', - delay: 1000, - iterationStart: 0.75, - easing: 'steps(4, start)' - }, - conditions: [ - { currentTime: 0, progress: 0.75 }, - { currentTime: 999, progress: 0.75 }, - { currentTime: 1000, progress: 1 }, - { currentTime: 2000, progress: 1 }, - { currentTime: 2500, progress: 1 } - ] - }, - { - description: 'Test bounds point of step-start easing ' + - 'with alternate direction', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - duration: 1000, - fill: 'both', - delay: 1000, - iterations: 2, - iterationStart: 1.5, - direction: 'alternate', - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 1 }, - { currentTime: 1000, progress: 1 }, - { currentTime: 1001, progress: 0.5 }, - { currentTime: 2999, progress: 1 }, - { currentTime: 3000, progress: 0.5 }, - { currentTime: 3500, progress: 0.5 } - ] - }, - { - description: 'Test bounds point of step-start easing ' + - 'with alternate-reverse direction', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - duration: 1000, - fill: 'both', - delay: 1000, - iterations: 2, - iterationStart: 0.5, - direction: 'alternate-reverse', - easing: 'steps(2, start)' - }, - conditions: [ - { currentTime: 0, progress: 1 }, - { currentTime: 1000, progress: 1 }, - { currentTime: 1001, progress: 0.5 }, - { currentTime: 2999, progress: 1 }, - { currentTime: 3000, progress: 0.5 }, - { currentTime: 3500, progress: 0.5 } - ] - }, - { - description: 'Test bounds point of step-start easing in keyframe', - keyframe: [ { width: '0px', easing: 'steps(2, start)' }, - { width: '100px' } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - }, - conditions: [ - { currentTime: 0, progress: 0, width: '0px' }, - { currentTime: 999, progress: 0, width: '0px' }, - { currentTime: 1000, progress: 0, width: '50px' }, - { currentTime: 1499, progress: 0.499, width: '50px' }, - { currentTime: 1500, progress: 0.5, width: '100px' }, - { currentTime: 2000, progress: 1, width: '100px' }, - { currentTime: 2500, progress: 1, width: '100px' } - ] - }, - { - description: 'Test bounds point of step-end easing ' + - 'with iterationStart and delay', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - duration: 1000, - fill: 'both', - delay: 1000, - iterationStart: 0.5, - easing: 'steps(2, end)' - }, - conditions: [ - { currentTime: 0, progress: 0 }, - { currentTime: 999, progress: 0 }, - { currentTime: 1000, progress: 0.5 }, - { currentTime: 1499, progress: 0.5 }, - { currentTime: 1500, progress: 0 }, - { currentTime: 1999, progress: 0 }, - { currentTime: 2000, progress: 0.5 }, - { currentTime: 2500, progress: 0.5 } - ] - }, - { - description: 'Test bounds point of step-end easing ' + - 'with iterationStart not at a transition point', - keyframe: [ { width: '0px' }, - { width: '100px' } ], - effect: { - delay: 1000, - duration: 1000, - fill: 'both', - iterationStart: 0.75, - easing: 'steps(2, end)' - }, - conditions: [ - { currentTime: 0, progress: 0.5 }, - { currentTime: 999, progress: 0.5 }, - { currentTime: 1000, progress: 0.5 }, - { currentTime: 1249, progress: 0.5 }, - { currentTime: 1250, progress: 0 }, - { currentTime: 1749, progress: 0 }, - { currentTime: 1750, progress: 0.5 }, - { currentTime: 2000, progress: 0.5 }, - { currentTime: 2500, progress: 0.5 }, - ] - } -]; - -gStepTimingFunctionTests.forEach(function(options) { - test(function(t) { - var target = createDiv(t); - var animation = target.animate(options.keyframe, options.effect); - options.conditions.forEach(function(condition) { - animation.currentTime = condition.currentTime; - if (typeof condition.progress !== 'undefined') { - assert_equals(animation.effect.getComputedTiming().progress, - condition.progress, - 'Progress at ' + animation.currentTime + 'ms'); - } - if (typeof condition.width !== 'undefined') { - assert_equals(getComputedStyle(target).width, - condition.width, - 'Progress at ' + animation.currentTime + 'ms'); - } - }); - }, options.description); -}); - -gInvalidEasingTests.forEach(function(options) { - test(function(t) { - var div = createDiv(t); - assert_throws({ name: 'TypeError' }, - function() { - div.animate({ easing: options.easing }, 100 * MS_PER_SEC); - }); - }, 'Invalid keyframe easing value: \'' + options.easing + '\''); -}); - -</script> -</body>
rename from testing/web-platform/tests/web-animations/resources/effect-easing-tests.js rename to testing/web-platform/tests/web-animations/resources/easing-tests.js --- a/testing/web-platform/tests/web-animations/resources/effect-easing-tests.js +++ b/testing/web-platform/tests/web-animations/resources/easing-tests.js @@ -1,9 +1,9 @@ -var gEffectEasingTests = [ +var gEasingTests = [ { desc: 'step-start function', easing: 'step-start', easingFunction: stepStart(1), serialization: 'steps(1, start)' }, { desc: 'steps(1, start) function', @@ -62,37 +62,26 @@ var gEffectEasingTests = [ desc: 'ease-out function', easing: 'ease-out', // cubic-bezier(0, 0, 0.58, 1.0) easingFunction: cubicBezier(0, 0, 0.58, 1.0) }, { desc: 'easing function which produces values greater than 1', easing: 'cubic-bezier(0, 1.5, 1, 1.5)', easingFunction: cubicBezier(0, 1.5, 1, 1.5) + }, + { + desc: 'easing function which produces values less than 1', + easing: 'cubic-bezier(0, -0.5, 1, -0.5)', + easingFunction: cubicBezier(0, -0.5, 1, -0.5) } ]; -var gInvalidEasingTests = [ - { - easing: '' - }, - { - easing: 'test' - }, - { - easing: 'cubic-bezier(1.1, 0, 1, 1)' - }, - { - easing: 'cubic-bezier(0, 0, 1.1, 1)' - }, - { - easing: 'cubic-bezier(-0.1, 0, 1, 1)' - }, - { - easing: 'cubic-bezier(0, 0, -0.1, 1)' - }, - { - easing: 'steps(-1, start)' - }, - { - easing: 'steps(0.1, start)' - }, +var gInvalidEasings = [ + '', + 'test', + 'cubic-bezier(1.1, 0, 1, 1)', + 'cubic-bezier(0, 0, 1.1, 1)', + 'cubic-bezier(-0.1, 0, 1, 1)', + 'cubic-bezier(0, 0, -0.1, 1)', + 'steps(-1, start)', + 'steps(0.1, start)' ];
new file mode 100644 --- /dev/null +++ b/testing/web-platform/tests/web-animations/timing-model/time-transformations/transformed-progress.html @@ -0,0 +1,272 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Tests for the transformed progress</title> +<link rel="help" href="https://w3c.github.io/web-animations/#calculating-the-transformed-progress"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../../testcommon.js"></script> +<script src="../../resources/easing-tests.js"></script> +<body> +<div id="log"></div> +<div id="target"></div> +<script> +'use strict'; + +gEasingTests.forEach(params => { + test(function(t) { + const target = createDiv(t); + const anim = target.animate(null, { duration: 1000, + fill: 'forwards', + easing: params.easing }); + + [ 0, 250, 500, 750, 1000 ].forEach(sampleTime => { + anim.currentTime = sampleTime; + const portion = sampleTime / anim.effect.getComputedTiming().duration; + const expectedProgress = params.easingFunction(portion); + assert_approx_equals(anim.effect.getComputedTiming().progress, + expectedProgress, + 0.01, + 'The progress should be approximately ' + + expectedProgress + ` at ${sampleTime}ms`); + }); + }, 'Transformed progress for ' + params.desc); +}); + +// Additional tests for various boundary conditions of step timing functions + +var gStepTimingFunctionTests = [ + { + description: 'Test bounds point of step-start easing', + effect: { + delay: 1000, + duration: 1000, + fill: 'both', + easing: 'steps(2, start)' + }, + conditions: [ + { currentTime: 0, progress: 0 }, + { currentTime: 999, progress: 0 }, + { currentTime: 1000, progress: 0.5 }, + { currentTime: 1499, progress: 0.5 }, + { currentTime: 1500, progress: 1 }, + { currentTime: 2000, progress: 1 } + ] + }, + { + description: 'Test bounds point of step-start easing with reverse direction', + effect: { + delay: 1000, + duration: 1000, + fill: 'both', + direction: 'reverse', + easing: 'steps(2, start)' + }, + conditions: [ + { currentTime: 0, progress: 1 }, + { currentTime: 1001, progress: 1 }, + { currentTime: 1500, progress: 1 }, + { currentTime: 1501, progress: 0.5 }, + { currentTime: 2000, progress: 0 }, + { currentTime: 2500, progress: 0 } + ] + }, + { + description: 'Test bounds point of step-start easing ' + + 'with iterationStart not at a transition point', + effect: { + delay: 1000, + duration: 1000, + fill: 'both', + iterationStart: 0.25, + easing: 'steps(2, start)' + }, + conditions: [ + { currentTime: 0, progress: 0.5 }, + { currentTime: 999, progress: 0.5 }, + { currentTime: 1000, progress: 0.5 }, + { currentTime: 1249, progress: 0.5 }, + { currentTime: 1250, progress: 1 }, + { currentTime: 1749, progress: 1 }, + { currentTime: 1750, progress: 0.5 }, + { currentTime: 2000, progress: 0.5 }, + { currentTime: 2500, progress: 0.5 }, + ] + }, + { + description: 'Test bounds point of step-start easing ' + + 'with iterationStart and delay', + effect: { + delay: 1000, + duration: 1000, + fill: 'both', + iterationStart: 0.5, + easing: 'steps(2, start)' + }, + conditions: [ + { currentTime: 0, progress: 0.5 }, + { currentTime: 999, progress: 0.5 }, + { currentTime: 1000, progress: 1 }, + { currentTime: 1499, progress: 1 }, + { currentTime: 1500, progress: 0.5 }, + { currentTime: 2000, progress: 1 } + ] + }, + { + description: 'Test bounds point of step-start easing ' + + 'with iterationStart and reverse direction', + effect: { + delay: 1000, + duration: 1000, + fill: 'both', + iterationStart: 0.5, + direction: 'reverse', + easing: 'steps(2, start)' + }, + conditions: [ + { currentTime: 0, progress: 1 }, + { currentTime: 1000, progress: 1 }, + { currentTime: 1001, progress: 0.5 }, + { currentTime: 1499, progress: 0.5 }, + { currentTime: 1500, progress: 1 }, + { currentTime: 1999, progress: 1 }, + { currentTime: 2000, progress: 0.5 }, + { currentTime: 2500, progress: 0.5 } + ] + }, + { + description: 'Test bounds point of step(4, start) easing ' + + 'with iterationStart 0.75 and delay', + effect: { + duration: 1000, + fill: 'both', + delay: 1000, + iterationStart: 0.75, + easing: 'steps(4, start)' + }, + conditions: [ + { currentTime: 0, progress: 0.75 }, + { currentTime: 999, progress: 0.75 }, + { currentTime: 1000, progress: 1 }, + { currentTime: 2000, progress: 1 }, + { currentTime: 2500, progress: 1 } + ] + }, + { + description: 'Test bounds point of step-start easing ' + + 'with alternate direction', + effect: { + duration: 1000, + fill: 'both', + delay: 1000, + iterations: 2, + iterationStart: 1.5, + direction: 'alternate', + easing: 'steps(2, start)' + }, + conditions: [ + { currentTime: 0, progress: 1 }, + { currentTime: 1000, progress: 1 }, + { currentTime: 1001, progress: 0.5 }, + { currentTime: 2999, progress: 1 }, + { currentTime: 3000, progress: 0.5 }, + { currentTime: 3500, progress: 0.5 } + ] + }, + { + description: 'Test bounds point of step-start easing ' + + 'with alternate-reverse direction', + effect: { + duration: 1000, + fill: 'both', + delay: 1000, + iterations: 2, + iterationStart: 0.5, + direction: 'alternate-reverse', + easing: 'steps(2, start)' + }, + conditions: [ + { currentTime: 0, progress: 1 }, + { currentTime: 1000, progress: 1 }, + { currentTime: 1001, progress: 0.5 }, + { currentTime: 2999, progress: 1 }, + { currentTime: 3000, progress: 0.5 }, + { currentTime: 3500, progress: 0.5 } + ] + }, + { + description: 'Test bounds point of step-end easing', + effect: { + delay: 1000, + duration: 1000, + fill: 'both', + easing: 'steps(2, end)' + }, + conditions: [ + { currentTime: 0, progress: 0 }, + { currentTime: 999, progress: 0 }, + { currentTime: 1000, progress: 0 }, + { currentTime: 1499, progress: 0 }, + { currentTime: 1500, progress: 0.5 }, + { currentTime: 2000, progress: 1 } + ] + }, + { + description: 'Test bounds point of step-end easing ' + + 'with iterationStart and delay', + effect: { + duration: 1000, + fill: 'both', + delay: 1000, + iterationStart: 0.5, + easing: 'steps(2, end)' + }, + conditions: [ + { currentTime: 0, progress: 0 }, + { currentTime: 999, progress: 0 }, + { currentTime: 1000, progress: 0.5 }, + { currentTime: 1499, progress: 0.5 }, + { currentTime: 1500, progress: 0 }, + { currentTime: 1999, progress: 0 }, + { currentTime: 2000, progress: 0.5 }, + { currentTime: 2500, progress: 0.5 } + ] + }, + { + description: 'Test bounds point of step-end easing ' + + 'with iterationStart not at a transition point', + effect: { + delay: 1000, + duration: 1000, + fill: 'both', + iterationStart: 0.75, + easing: 'steps(2, end)' + }, + conditions: [ + { currentTime: 0, progress: 0.5 }, + { currentTime: 999, progress: 0.5 }, + { currentTime: 1000, progress: 0.5 }, + { currentTime: 1249, progress: 0.5 }, + { currentTime: 1250, progress: 0 }, + { currentTime: 1749, progress: 0 }, + { currentTime: 1750, progress: 0.5 }, + { currentTime: 2000, progress: 0.5 }, + { currentTime: 2500, progress: 0.5 }, + ] + } +]; + +gStepTimingFunctionTests.forEach(function(options) { + test(function(t) { + var target = createDiv(t); + var animation = target.animate(null, options.effect); + options.conditions.forEach(function(condition) { + animation.currentTime = condition.currentTime; + assert_equals(animation.effect.getComputedTiming().progress, + condition.progress, + 'Progress at ' + animation.currentTime + 'ms'); + }); + }, options.description); +}); + +</script> +</body>
--- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -15,17 +15,16 @@ #include "nsAnnotationService.h" #include "nsFaviconService.h" #include "nsPlacesMacros.h" #include "History.h" #include "Helpers.h" #include "nsTArray.h" #include "nsCollationCID.h" -#include "nsILocaleService.h" #include "nsNetUtil.h" #include "nsPrintfCString.h" #include "nsPromiseFlatString.h" #include "nsString.h" #include "nsUnicharUtils.h" #include "prsystem.h" #include "prtime.h" #include "nsEscape.h" @@ -4474,28 +4473,21 @@ nsNavHistory::AutoCompleteFeedback(int32 nsICollation * nsNavHistory::GetCollation() { if (mCollation) return mCollation; - // locale - nsCOMPtr<nsILocale> locale; - nsCOMPtr<nsILocaleService> ls(do_GetService(NS_LOCALESERVICE_CONTRACTID)); - NS_ENSURE_TRUE(ls, nullptr); - nsresult rv = ls->GetApplicationLocale(getter_AddRefs(locale)); - NS_ENSURE_SUCCESS(rv, nullptr); - // collation nsCOMPtr<nsICollationFactory> cfact = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); NS_ENSURE_TRUE(cfact, nullptr); - rv = cfact->CreateCollation(locale, getter_AddRefs(mCollation)); + nsresult rv = cfact->CreateCollation(getter_AddRefs(mCollation)); NS_ENSURE_SUCCESS(rv, nullptr); return mCollation; } nsIStringBundle * nsNavHistory::GetBundle() {
--- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -3863,22 +3863,19 @@ SearchService.prototype = { let alphaEngines = []; for (let name in this._engines) { let engine = this._engines[name]; if (!(engine.name in addedEngines)) alphaEngines.push(this._engines[engine.name]); } - let locale = Cc["@mozilla.org/intl/nslocaleservice;1"] - .getService(Ci.nsILocaleService) - .newLocale(getLocale()); let collation = Cc["@mozilla.org/intl/collation-factory;1"] .createInstance(Ci.nsICollationFactory) - .CreateCollation(locale); + .CreateCollation(); const strength = Ci.nsICollation.kCollationCaseInsensitiveAscii; let comparator = (a, b) => collation.compareString(strength, a.name, b.name); alphaEngines.sort(comparator); return this.__sortedEngines = this.__sortedEngines.concat(alphaEngines); }, /** * Get a sorted array of engines.
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -9226,22 +9226,30 @@ "keyed": true, "kind": "exponential", "high": 10000, "n_buckets": 100, "bug_numbers": [1294349] }, "VIDEO_AS_CONTENT_SOURCE" : { "alert_emails": ["ajones@mozilla.com", "kaku@mozilla.com"], - "expires_in_version": "56", + "expires_in_version": "58", "description": "Usage of a {visible / invisible} video element as the source of {drawImage(), createPattern(), createImageBitmap() and captureStream()} APIs. (0 = ALL_VISIBLE, 1 = ALL_INVISIBLE, 2 = drawImage_VISIBLE, 3 = drawImage_INVISIBLE, 4 = createPattern_VISIBLE, 5 = createPattern_INVISIBLE, 6 = createImageBitmap_VISIBLE, 7 = createImageBitmap_INVISIBLE, 8 = captureStream_VISIBLE, 9 = captureStream_INVISIBLE)", "kind": "enumerated", "n_values": 12, "bug_numbers": [1299718] }, + "VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT" : { + "alert_emails": ["ajones@mozilla.com", "kaku@mozilla.com"], + "expires_in_version": "58", + "description": "Usage of an invisible {in tree / not in tree} video element as the source of {drawImage(), createPattern(), createImageBitmap() and captureStream()} APIs. (0 = ALL_IN_TREE, 1 = ALL_NOT_IN_TREE, 2 = drawImage_IN_TREE, 3 = drawImage_NOT_IN_TREE, 4 = createPattern_IN_TREE, 5 = createPattern_NOT_IN_TREE, 6 = createImageBitmap_IN_TREE, 7 = createImageBitmap_NOT_IN_TREE, 8 = captureStream_IN_TREE, 9 = captureStream_NOT_IN_TREE)", + "kind": "enumerated", + "n_values": 12, + "bug_numbers": [1337301] + }, "VIDEO_UNLOAD_STATE": { "alert_emails": ["ajones@mozilla.com"], "expires_in_version": "55", "kind": "enumerated", "n_values": 5, "description": "HTML Media Element state when unloading. ended = 0, paused = 1, stalled = 2, seeking = 3, other = 4", "bug_numbers": [1261955, 1261955] },
--- a/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js +++ b/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js @@ -84,17 +84,17 @@ const DISALLOWED = { flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_LOWERED, // Renamed to alwaysLowered defaults_to: false, }, "alwaysRaised": { flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_RAISED, defaults_to: false, }, "suppressanimation": { - flag: Ci.nsIWebBrowserChrome.CHROME_MAC_SUPPRESS_ANIMATION, + flag: Ci.nsIWebBrowserChrome.CHROME_SUPPRESS_ANIMATION, defaults_to: false, }, "extrachrome": { flag: Ci.nsIWebBrowserChrome.CHROME_EXTRA, defaults_to: false, }, "centerscreen": { flag: Ci.nsIWebBrowserChrome.CHROME_CENTER_SCREEN, @@ -187,16 +187,17 @@ function assertContentFlags(chromeFlags) // have been able to flip it on. Assert.ok((chromeFlags & flag), `Expected feature ${feature} to be enabled`); } } for (let feature in DISALLOWED) { let flag = DISALLOWED[feature].flag; + Assert.ok(flag, "Expected flag to be a non-zeroish value"); if (DISALLOWED[feature].defaults_to) { // The feature is supposed to default to true, so it should // stay true. Assert.ok((chromeFlags & flag), `Expected feature ${feature} to be unchanged`); } else { // The feature is supposed to default to false, so it should // stay false.
--- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -9,9 +9,9 @@ include $(topsrcdir)/config/config.mk # Wrap linker to print linking status periodically to prevent the linking # process from getting killed EXPAND_LIBS_EXEC := $(PYTHON) $(topsrcdir)/config/link.py include $(topsrcdir)/config/rules.mk .PHONY: gtestxul gtestxul: - $(MAKE) -C $(DEPTH) toolkit/library/gtest/target LINK_GTEST=1 + $(MAKE) -C $(DEPTH) toolkit/library/gtest/target LINK_GTEST_DURING_COMPILE=1
--- a/toolkit/library/dependentlibs.py +++ b/toolkit/library/dependentlibs.py @@ -121,15 +121,21 @@ def gen_list(output, lib): else: ext = os.path.splitext(lib)[1] assert(ext == '.dll') func = dependentlibs_dumpbin deps = dependentlibs(lib, libpaths, func) deps[lib] = mozpath.join(libpaths[0], lib) output.write('\n'.join(deps.keys()) + '\n') + + with open(output.name + ".gtest", 'w') as gtest_out: + libs = deps.keys() + libs[-1] = 'gtest/' + libs[-1] + gtest_out.write('\n'.join(libs) + '\n') + return set(deps.values()) def main(): gen_list(sys.stdout, sys.argv[1]) if __name__ == '__main__': main()
--- a/toolkit/library/gtest/Makefile.in +++ b/toolkit/library/gtest/Makefile.in @@ -1,35 +1,28 @@ # 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/. # Enforce that the clean/distclean rules removes everything that needs # to be removed from this directory. ifneq (,$(filter clean distclean,$(MAKECMDGOALS))) -LINK_GTEST = 1 +LINK_GTEST_DURING_COMPILE = 1 endif -ifndef LINK_GTEST -# Force to not include backend.mk unless LINK_GTEST is defined. +ifndef LINK_GTEST_DURING_COMPILE +# Force to not include backend.mk unless LINK_GTEST_DURING_COMPILE is set. # Not including backend.mk makes traversing this directory do nothing. STANDALONE_MAKEFILE = 1 else include $(topsrcdir)/toolkit/library/libxul.mk include $(topsrcdir)/config/config.mk # Wrap linker to print linking status periodically to prevent the linking # process from getting killed EXPAND_LIBS_EXEC := $(PYTHON) $(topsrcdir)/config/link.py -ifdef COMPILE_ENVIRONMENT -target:: $(DIST)/bin/dependentlibs.list.gtest -endif - -$(DIST)/bin/dependentlibs.list.gtest: $(DIST)/bin/dependentlibs.list - sed -e 's|$(SHARED_LIBRARY)|gtest/$(SHARED_LIBRARY)|' $< > $@ - LINK_PDBFILE = xul-gtest.pdb endif
--- a/toolkit/library/moz.build +++ b/toolkit/library/moz.build @@ -373,22 +373,23 @@ if CONFIG['COMPILE_ENVIRONMENT']: if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'): full_libname = SHARED_LIBRARY_NAME else: full_libname = '%s%s%s' % ( CONFIG['DLL_PREFIX'], LIBRARY_NAME, CONFIG['DLL_SUFFIX'] ) - GENERATED_FILES += ['dependentlibs.list'] - GENERATED_FILES['dependentlibs.list'].script = 'dependentlibs.py:gen_list' - GENERATED_FILES['dependentlibs.list'].inputs = [ + GENERATED_FILES += [('dependentlibs.list', 'dependentlibs.list.gtest')] + dep_libs_list = GENERATED_FILES[('dependentlibs.list', 'dependentlibs.list.gtest')] + dep_libs_list.script = 'dependentlibs.py:gen_list' + dep_libs_list.inputs = [ '!%s' % full_libname, ] - FINAL_TARGET_FILES += ['!dependentlibs.list'] + FINAL_TARGET_FILES += ['!dependentlibs.list', '!dependentlibs.list.gtest'] # WebRender dependencies if CONFIG['MOZ_ENABLE_WEBRENDER']: if CONFIG['OS_ARCH'] == 'Linux': OS_LIBS += [ 'GL', ] elif CONFIG['OS_ARCH'] == 'WINNT':
--- a/widget/cocoa/TextInputHandler.mm +++ b/widget/cocoa/TextInputHandler.mm @@ -3752,17 +3752,34 @@ IMEInputHandler::SendCommittedText(NSStr NS_ENSURE_TRUE(mWidget, ); // XXX We should send the string without mView. if (!mView) { return; } NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:aString]; - [mView insertText:attrStr]; + if ([mView conformsToProtocol:@protocol(NSTextInputClient)]) { + NSObject<NSTextInputClient>* textInputClient = + static_cast<NSObject<NSTextInputClient>*>(mView); + [textInputClient insertText:attrStr + replacementRange:NSMakeRange(NSNotFound, 0)]; + } + + // Last resort. If we cannot retrieve NSTextInputProtocol from mView + // or blocking to call our InsertText(), we should call InsertText() + // directly to commit composition forcibly. + if (mIsIMEComposing) { + MOZ_LOG(gLog, LogLevel::Info, + ("%p IMEInputHandler::SendCommittedText, trying to insert text directly " + "due to IME not calling our InsertText()", this)); + static_cast<TextInputHandler*>(this)->InsertText(attrStr); + MOZ_ASSERT(!mIsIMEComposing); + } + [attrStr release]; NS_OBJC_END_TRY_ABORT_BLOCK; } void IMEInputHandler::KillIMEComposition() {