diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 7745d9524fd7..dd4022037557 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -3022,7 +3022,8 @@ var BrowserOnClick = { case "Browser:CertExceptionError": this.onCertError(msg.target, msg.data.elementId, msg.data.isTopFrame, msg.data.location, - msg.data.securityInfoAsString); + msg.data.securityInfoAsString, + msg.data.frameId); break; case "Browser:OpenCaptivePortalPage": CaptivePortalWatcher.ensureCaptivePortalTab(); @@ -3067,7 +3068,7 @@ var BrowserOnClick = { } }, - onCertError(browser, elementId, isTopFrame, location, securityInfoAsString) { + onCertError(browser, elementId, isTopFrame, location, securityInfoAsString, frameId) { let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI"); let securityInfo; @@ -3118,9 +3119,10 @@ var BrowserOnClick = { securityInfo = getSecurityInfo(securityInfoAsString); let errorInfo = getDetailedCertErrorInfo(location, securityInfo); - browser.messageManager.sendAsyncMessage( "CertErrorDetails", { + browser.messageManager.sendAsyncMessage("CertErrorDetails", { code: securityInfo.errorCode, - info: errorInfo + info: errorInfo, + frameId, }); break; diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 0553fbf2ca28..33d60b28ca44 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -234,30 +234,35 @@ var AboutNetAndCertErrorListener = { chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true); }, - get isAboutNetError() { - return content.document.documentURI.startsWith("about:neterror"); + isAboutNetError(doc) { + return doc.documentURI.startsWith("about:neterror"); }, - get isAboutCertError() { - return content.document.documentURI.startsWith("about:certerror"); + isAboutCertError(doc) { + return doc.documentURI.startsWith("about:certerror"); }, receiveMessage(msg) { - if (!this.isAboutCertError) { - return; - } + if (msg.name == "CertErrorDetails") { + let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell); + // We need nsIWebNavigation to access docShell.document. + frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation); + if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) { + return; + } - switch (msg.name) { - case "CertErrorDetails": - this.onCertErrorDetails(msg); - break; - case "Browser:CaptivePortalFreed": - this.onCaptivePortalFreed(msg); - break; + this.onCertErrorDetails(msg, frameDocShell); + } else if (msg.name == "Browser:CaptivePortalFreed") { + // TODO: This check is not correct for frames. + if (!this.isAboutCertError(content.document)) { + return; + } + + this.onCaptivePortalFreed(msg); } }, - _getCertValidityRange() { + _getCertValidityRange(docShell) { let {securityInfo} = docShell.failedChannel; securityInfo.QueryInterface(Ci.nsITransportSecurityInfo); let certs = securityInfo.failedCertChain.getEnumerator(); @@ -275,10 +280,12 @@ var AboutNetAndCertErrorListener = { return {notBefore, notAfter}; }, - onCertErrorDetails(msg) { - let div = content.document.getElementById("certificateErrorText"); + onCertErrorDetails(msg, docShell) { + let doc = docShell.document; + + let div = doc.getElementById("certificateErrorText"); div.textContent = msg.data.info; - let learnMoreLink = content.document.getElementById("learnMoreLink"); + let learnMoreLink = doc.getElementById("learnMoreLink"); let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); switch (msg.data.code) { @@ -301,7 +308,7 @@ var AboutNetAndCertErrorListener = { let lastFetched = Services.prefs.getIntPref(PREF_BLOCKLIST_LAST_FETCHED, 0) * 1000; let now = Date.now(); - let certRange = this._getCertValidityRange(); + let certRange = this._getCertValidityRange(docShell); let approximateDate = now - difference * 1000; // If the difference is more than a day, we last fetched the date in the last 5 days, @@ -315,17 +322,12 @@ var AboutNetAndCertErrorListener = { // negative difference means local time is behind server time approximateDate = formatter.format(new Date(approximateDate)); - content.document.getElementById("wrongSystemTime_URL") - .textContent = content.document.location.hostname; - content.document.getElementById("wrongSystemTime_systemDate") - .textContent = systemDate; - content.document.getElementById("wrongSystemTime_actualDate") - .textContent = approximateDate; + doc.getElementById("wrongSystemTime_URL").textContent = doc.location.hostname; + doc.getElementById("wrongSystemTime_systemDate").textContent = systemDate; + doc.getElementById("wrongSystemTime_actualDate").textContent = approximateDate; - content.document.getElementById("errorShortDesc") - .style.display = "none"; - content.document.getElementById("wrongSystemTimePanel") - .style.display = "block"; + doc.getElementById("errorShortDesc").style.display = "none"; + doc.getElementById("wrongSystemTimePanel").style.display = "block"; // If there is no clock skew with Kinto servers, check against the build date. // (The Kinto ping could have happened when the time was still right, or not at all) @@ -348,15 +350,13 @@ var AboutNetAndCertErrorListener = { dateStyle: "short" }); - content.document.getElementById("wrongSystemTimeWithoutReference_URL") - .textContent = content.document.location.hostname; - content.document.getElementById("wrongSystemTimeWithoutReference_systemDate") + doc.getElementById("wrongSystemTimeWithoutReference_URL") + .textContent = doc.location.hostname; + doc.getElementById("wrongSystemTimeWithoutReference_systemDate") .textContent = formatter.format(systemDate); - content.document.getElementById("errorShortDesc") - .style.display = "none"; - content.document.getElementById("wrongSystemTimeWithoutReferencePanel") - .style.display = "block"; + doc.getElementById("errorShortDesc").style.display = "none"; + doc.getElementById("wrongSystemTimeWithoutReferencePanel").style.display = "block"; } } learnMoreLink.href = baseURL + "time-errors"; @@ -369,13 +369,20 @@ var AboutNetAndCertErrorListener = { }, handleEvent(aEvent) { - if (!this.isAboutNetError && !this.isAboutCertError) { + let doc; + if (aEvent.originalTarget instanceof Ci.nsIDOMDocument) { + doc = aEvent.originalTarget; + } else { + doc = aEvent.originalTarget.ownerDocument; + } + + if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) { return; } switch (aEvent.type) { case "AboutNetErrorLoad": - this.onPageLoad(aEvent); + this.onPageLoad(aEvent.originalTarget, doc.defaultView); break; case "AboutNetErrorOpenCaptivePortal": this.openCaptivePortalPage(aEvent); @@ -402,18 +409,16 @@ var AboutNetAndCertErrorListener = { return false; }, - onPageLoad(evt) { + onPageLoad(originalTarget, win) { // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0; - if (this.isAboutCertError) { - let originalTarget = evt.originalTarget; - let ownerDoc = originalTarget.ownerDocument; - ClickEventHandler.onCertError(originalTarget, ownerDoc); + if (this.isAboutCertError(win.document)) { + ClickEventHandler.onCertError(originalTarget, win); } let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic"); - content.dispatchEvent(new content.CustomEvent("AboutNetErrorOptions", { + win.dispatchEvent(new win.CustomEvent("AboutNetErrorOptions", { detail: JSON.stringify({ enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"), changedCertPrefs: this.changedCertPrefs(), @@ -442,9 +447,7 @@ var AboutNetAndCertErrorListener = { // If we're enabling reports, send a report for this failure. if (evt.detail) { let win = evt.originalTarget.ownerGlobal; - let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); + let docShell = win.document.docShell; let {securityInfo} = docShell.failedChannel; securityInfo.QueryInterface(Ci.nsITransportSecurityInfo); @@ -478,13 +481,13 @@ var ClickEventHandler = { // Handle click events from about pages if (event.button == 0) { - if (ownerDoc.documentURI.startsWith("about:certerror")) { - this.onCertError(originalTarget, ownerDoc); + if (AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) { + this.onCertError(originalTarget, ownerDoc.defaultView); return; } else if (ownerDoc.documentURI.startsWith("about:blocked")) { this.onAboutBlocked(originalTarget, ownerDoc); return; - } else if (ownerDoc.documentURI.startsWith("about:neterror")) { + } else if (AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) { this.onAboutNetError(event, ownerDoc.documentURI); return; } @@ -538,9 +541,7 @@ var ClickEventHandler = { // Only when the owner doc has |mixedContentChannel| and the same origin // should we allow mixed content. json.allowMixedContent = false; - let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); + let docshell = ownerDoc.docShell; if (docShell.mixedContentChannel) { const sm = Services.scriptSecurityManager; try { @@ -562,14 +563,13 @@ var ClickEventHandler = { } }, - onCertError(targetElement, ownerDoc) { - let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); + onCertError(targetElement, win) { + let docShell = win.document.docShell; sendAsyncMessage("Browser:CertExceptionError", { - location: ownerDoc.location.href, + frameId: WebNavigationFrames.getFrameId(win), + location: win.document.location.href, elementId: targetElement.getAttribute("id"), - isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView), + isTopFrame: (win.parent === win), securityInfoAsString: getSerializedSecurityInfo(docShell), }); }, diff --git a/browser/base/content/test/about/browser.ini b/browser/base/content/test/about/browser.ini index 1655d001b67c..25fb829e8dbe 100644 --- a/browser/base/content/test/about/browser.ini +++ b/browser/base/content/test/about/browser.ini @@ -9,6 +9,8 @@ support-files = POSTSearchEngine.xml [browser_aboutCertError.js] +support-files = + dummy_page.html [browser_aboutHome_imitate.js] [browser_aboutHome_input.js] skip-if = true # Bug 1409054 to remove; previously skipped for intermittents, e.g., Bug 1399648 diff --git a/browser/base/content/test/about/browser_aboutCertError.js b/browser/base/content/test/about/browser_aboutCertError.js index 625f9d33da60..ee8a8a18e792 100644 --- a/browser/base/content/test/about/browser_aboutCertError.js +++ b/browser/base/content/test/about/browser_aboutCertError.js @@ -6,104 +6,150 @@ // This is testing the aboutCertError page (Bug 1207107). const GOOD_PAGE = "https://example.com/"; +const GOOD_PAGE_2 = "https://example.org/"; +const DUMMY_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", GOOD_PAGE) + "dummy_page.html"; const BAD_CERT = "https://expired.example.com/"; const UNKNOWN_ISSUER = "https://self-signed.example.com "; const BAD_STS_CERT = "https://badchain.include-subdomains.pinning.example.com:443"; const {TabStateFlusher} = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {}); const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); +function injectErrorPageFrame(tab, src) { + return ContentTask.spawn(tab.linkedBrowser, {frameSrc: src}, async function({frameSrc}) { + let loaded = ContentTaskUtils.waitForEvent(content.wrappedJSObject, "DOMFrameContentLoaded"); + let iframe = content.document.createElement("iframe"); + iframe.src = frameSrc; + content.document.body.appendChild(iframe); + await loaded; + // We will have race conditions when accessing the frame content after setting a src, + // so we can't wait for AboutNetErrorLoad. Let's wait for the certerror class to + // appear instead (which should happen at the same time as AboutNetErrorLoad). + await ContentTaskUtils.waitForCondition(() => + iframe.contentDocument.body.classList.contains("certerror")); + }); +} + +async function openErrorPage(src, useFrame) { + let tab; + if (useFrame) { + info("Loading cert error page in an iframe"); + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE); + await injectErrorPageFrame(tab, src); + } else { + let certErrorLoaded; + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, () => { + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, src); + let browser = gBrowser.selectedBrowser; + certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); + }, false); + info("Loading and waiting for the cert error"); + await certErrorLoaded; + } + + return tab; +} + add_task(async function checkReturnToAboutHome() { info("Loading a bad cert page directly and making sure 'return to previous page' goes to about:home"); - let browser; - let certErrorLoaded; - let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, () => { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, BAD_CERT); - browser = gBrowser.selectedBrowser; - certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); - }, false); + for (let useFrame of [false, true]) { + let tab = await openErrorPage(BAD_CERT, useFrame); + let browser = tab.linkedBrowser; - info("Loading and waiting for the cert error"); - await certErrorLoaded; + is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack"); + is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward"); - is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack"); - is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward"); + // Populate the shistory entries manually, since it happens asynchronously + // and the following tests will be too soon otherwise. + await TabStateFlusher.flush(browser); + let {entries} = JSON.parse(ss.getTabState(tab)); + is(entries.length, 1, "there is one shistory entry"); - // Populate the shistory entries manually, since it happens asynchronously - // and the following tests will be too soon otherwise. - await TabStateFlusher.flush(browser); - let {entries} = JSON.parse(ss.getTabState(tab)); - is(entries.length, 1, "there is one shistory entry"); + info("Clicking the go back button on about:certerror"); + await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; - info("Clicking the go back button on about:certerror"); - await ContentTask.spawn(browser, null, async function() { - let doc = content.document; - let returnButton = doc.getElementById("returnButton"); - is(returnButton.getAttribute("autofocus"), "true", "returnButton has autofocus"); - returnButton.click(); + let returnButton = doc.getElementById("returnButton"); + if (!frame) { + is(returnButton.getAttribute("autofocus"), "true", "returnButton has autofocus"); + } + returnButton.click(); - await ContentTaskUtils.waitForEvent(this, "pageshow", true); - }); + await ContentTaskUtils.waitForEvent(this, "pageshow", true); + }); - is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack"); - is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward"); - is(gBrowser.currentURI.spec, "about:home", "Went back"); + is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack"); + is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward"); + is(gBrowser.currentURI.spec, "about:home", "Went back"); - await BrowserTestUtils.removeTab(gBrowser.selectedTab); + await BrowserTestUtils.removeTab(gBrowser.selectedTab); + } }); add_task(async function checkReturnToPreviousPage() { info("Loading a bad cert page and making sure 'return to previous page' goes back"); - let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE); - let browser = gBrowser.selectedBrowser; + for (let useFrame of [false, true]) { + let tab; + let browser; + if (useFrame) { + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE); + browser = tab.linkedBrowser; - info("Loading and waiting for the cert error"); - let certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); - BrowserTestUtils.loadURI(browser, BAD_CERT); - await certErrorLoaded; + BrowserTestUtils.loadURI(browser, GOOD_PAGE_2); + await BrowserTestUtils.browserLoaded(browser, false, GOOD_PAGE_2); + await injectErrorPageFrame(tab, BAD_CERT); + } else { + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE); + browser = gBrowser.selectedBrowser; - is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack"); - is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward"); + info("Loading and waiting for the cert error"); + let certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); + BrowserTestUtils.loadURI(browser, BAD_CERT); + await certErrorLoaded; + } - // Populate the shistory entries manually, since it happens asynchronously - // and the following tests will be too soon otherwise. - await TabStateFlusher.flush(browser); - let {entries} = JSON.parse(ss.getTabState(tab)); - is(entries.length, 2, "there are two shistory entries"); + is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack"); + is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward"); - info("Clicking the go back button on about:certerror"); - await ContentTask.spawn(browser, null, async function() { - let doc = content.document; - let returnButton = doc.getElementById("returnButton"); - returnButton.click(); + // Populate the shistory entries manually, since it happens asynchronously + // and the following tests will be too soon otherwise. + await TabStateFlusher.flush(browser); + let {entries} = JSON.parse(ss.getTabState(tab)); + is(entries.length, 2, "there are two shistory entries"); - await ContentTaskUtils.waitForEvent(this, "pageshow", true); - }); + info("Clicking the go back button on about:certerror"); + await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; + let returnButton = doc.getElementById("returnButton"); + returnButton.click(); - is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack"); - is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward"); - is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back"); + await ContentTaskUtils.waitForEvent(this, "pageshow", true); + }); - await BrowserTestUtils.removeTab(gBrowser.selectedTab); + is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack"); + is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward"); + is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back"); + + await BrowserTestUtils.removeTab(gBrowser.selectedTab); + } }); add_task(async function checkBadStsCert() { info("Loading a badStsCert and making sure exception button doesn't show up"); - await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE); - let browser = gBrowser.selectedBrowser; - info("Loading and waiting for the cert error"); - let certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); - BrowserTestUtils.loadURI(browser, BAD_STS_CERT); - await certErrorLoaded; + for (let useFrame of [false, true]) { + let tab = await openErrorPage(BAD_STS_CERT, useFrame); + let browser = tab.linkedBrowser; - let exceptionButtonHidden = await ContentTask.spawn(browser, null, async function() { - let doc = content.document; - let exceptionButton = doc.getElementById("exceptionDialogButton"); - return exceptionButton.hidden; - }); - ok(exceptionButtonHidden, "Exception button is hidden"); + let exceptionButtonHidden = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; + let exceptionButton = doc.getElementById("exceptionDialogButton"); + return exceptionButton.hidden; + }); - await BrowserTestUtils.removeTab(gBrowser.selectedTab); + ok(exceptionButtonHidden, "Exception button is hidden"); + + await BrowserTestUtils.removeTab(gBrowser.selectedTab); + } }); // This checks that the appinfo.appBuildID starts with a date string, @@ -221,155 +267,142 @@ add_task(async function checkWrongSystemTimeWarning() { add_task(async function checkAdvancedDetails() { info("Loading a bad cert page and verifying the main error and advanced details section"); - let browser; - let certErrorLoaded; - await BrowserTestUtils.openNewForegroundTab(gBrowser, () => { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, BAD_CERT); - browser = gBrowser.selectedBrowser; - certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); - }, false); + for (let useFrame of [false, true]) { + let tab = await openErrorPage(BAD_CERT, useFrame); + let browser = tab.linkedBrowser; - info("Loading and waiting for the cert error"); - await certErrorLoaded; + let message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; - let message = await ContentTask.spawn(browser, null, async function() { - let doc = content.document; - let shortDescText = doc.getElementById("errorShortDescText"); - info("Main error text: " + shortDescText.textContent); - ok(shortDescText.textContent.includes("expired.example.com"), - "Should list hostname in error message."); + let shortDescText = doc.getElementById("errorShortDescText"); + info("Main error text: " + shortDescText.textContent); + ok(shortDescText.textContent.includes("expired.example.com"), + "Should list hostname in error message."); - let advancedButton = doc.getElementById("advancedButton"); - advancedButton.click(); - let el = doc.getElementById("errorCode"); - return { textContent: el.textContent, tagName: el.tagName }; - }); - is(message.textContent, "SEC_ERROR_EXPIRED_CERTIFICATE", - "Correct error message found"); - is(message.tagName, "a", "Error message is a link"); + let advancedButton = doc.getElementById("advancedButton"); + advancedButton.click(); + let el = doc.getElementById("errorCode"); + return { textContent: el.textContent, tagName: el.tagName }; + }); + is(message.textContent, "SEC_ERROR_EXPIRED_CERTIFICATE", + "Correct error message found"); + is(message.tagName, "a", "Error message is a link"); - message = await ContentTask.spawn(browser, null, async function() { - let doc = content.document; - let errorCode = doc.getElementById("errorCode"); - errorCode.click(); - let div = doc.getElementById("certificateErrorDebugInformation"); - let text = doc.getElementById("certificateErrorText"); + message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; - let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] - .getService(Ci.nsISerializationHelper); - let serializable = docShell.failedChannel.securityInfo - .QueryInterface(Ci.nsITransportSecurityInfo) - .QueryInterface(Ci.nsISerializable); - let serializedSecurityInfo = serhelper.serializeToString(serializable); - return { - divDisplay: content.getComputedStyle(div).display, - text: text.textContent, - securityInfoAsString: serializedSecurityInfo - }; - }); - isnot(message.divDisplay, "none", "Debug information is visible"); - ok(message.text.includes(BAD_CERT), "Correct URL found"); - ok(message.text.includes("Certificate has expired"), - "Correct error message found"); - ok(message.text.includes("HTTP Strict Transport Security: false"), - "Correct HSTS value found"); - ok(message.text.includes("HTTP Public Key Pinning: false"), - "Correct HPKP value found"); - let certChain = getCertChain(message.securityInfoAsString); - ok(message.text.includes(certChain), "Found certificate chain"); + let errorCode = doc.getElementById("errorCode"); + errorCode.click(); + let div = doc.getElementById("certificateErrorDebugInformation"); + let text = doc.getElementById("certificateErrorText"); - await BrowserTestUtils.removeTab(gBrowser.selectedTab); + let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] + .getService(Ci.nsISerializationHelper); + let serializable = doc.docShell.failedChannel.securityInfo + .QueryInterface(Ci.nsITransportSecurityInfo) + .QueryInterface(Ci.nsISerializable); + let serializedSecurityInfo = serhelper.serializeToString(serializable); + return { + divDisplay: content.getComputedStyle(div).display, + text: text.textContent, + securityInfoAsString: serializedSecurityInfo + }; + }); + isnot(message.divDisplay, "none", "Debug information is visible"); + ok(message.text.includes(BAD_CERT), "Correct URL found"); + ok(message.text.includes("Certificate has expired"), + "Correct error message found"); + ok(message.text.includes("HTTP Strict Transport Security: false"), + "Correct HSTS value found"); + ok(message.text.includes("HTTP Public Key Pinning: false"), + "Correct HPKP value found"); + let certChain = getCertChain(message.securityInfoAsString); + ok(message.text.includes(certChain), "Found certificate chain"); + + await BrowserTestUtils.removeTab(gBrowser.selectedTab); + } }); add_task(async function checkAdvancedDetailsForHSTS() { info("Loading a bad STS cert page and verifying the advanced details section"); - let browser; - let certErrorLoaded; - await BrowserTestUtils.openNewForegroundTab(gBrowser, () => { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, BAD_STS_CERT); - browser = gBrowser.selectedBrowser; - certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); - }, false); + for (let useFrame of [false, true]) { + let tab = await openErrorPage(BAD_STS_CERT, useFrame); + let browser = tab.linkedBrowser; - info("Loading and waiting for the cert error"); - await certErrorLoaded; + let message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; - let message = await ContentTask.spawn(browser, null, async function() { - let doc = content.document; - let advancedButton = doc.getElementById("advancedButton"); - advancedButton.click(); - let ec = doc.getElementById("errorCode"); - let cdl = doc.getElementById("cert_domain_link"); - return { - ecTextContent: ec.textContent, - ecTagName: ec.tagName, - cdlTextContent: cdl.textContent, - cdlTagName: cdl.tagName - }; - }); + let advancedButton = doc.getElementById("advancedButton"); + advancedButton.click(); + let ec = doc.getElementById("errorCode"); + let cdl = doc.getElementById("cert_domain_link"); + return { + ecTextContent: ec.textContent, + ecTagName: ec.tagName, + cdlTextContent: cdl.textContent, + cdlTagName: cdl.tagName + }; + }); - const badStsUri = Services.io.newURI(BAD_STS_CERT); - is(message.ecTextContent, "SSL_ERROR_BAD_CERT_DOMAIN", - "Correct error message found"); - is(message.ecTagName, "a", "Error message is a link"); - const url = badStsUri.prePath.slice(badStsUri.prePath.indexOf(".") + 1); - is(message.cdlTextContent, url, - "Correct cert_domain_link contents found"); - is(message.cdlTagName, "a", "cert_domain_link is a link"); + const badStsUri = Services.io.newURI(BAD_STS_CERT); + is(message.ecTextContent, "SSL_ERROR_BAD_CERT_DOMAIN", + "Correct error message found"); + is(message.ecTagName, "a", "Error message is a link"); + const url = badStsUri.prePath.slice(badStsUri.prePath.indexOf(".") + 1); + is(message.cdlTextContent, url, + "Correct cert_domain_link contents found"); + is(message.cdlTagName, "a", "cert_domain_link is a link"); - message = await ContentTask.spawn(browser, null, async function() { - let doc = content.document; - let errorCode = doc.getElementById("errorCode"); - errorCode.click(); - let div = doc.getElementById("certificateErrorDebugInformation"); - let text = doc.getElementById("certificateErrorText"); + message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; - let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] - .getService(Ci.nsISerializationHelper); - let serializable = docShell.failedChannel.securityInfo - .QueryInterface(Ci.nsITransportSecurityInfo) - .QueryInterface(Ci.nsISerializable); - let serializedSecurityInfo = serhelper.serializeToString(serializable); - return { - divDisplay: content.getComputedStyle(div).display, - text: text.textContent, - securityInfoAsString: serializedSecurityInfo - }; - }); - isnot(message.divDisplay, "none", "Debug information is visible"); - ok(message.text.includes(badStsUri.spec), "Correct URL found"); - ok(message.text.includes("requested domain name does not match the server\u2019s certificate"), - "Correct error message found"); - ok(message.text.includes("HTTP Strict Transport Security: false"), - "Correct HSTS value found"); - ok(message.text.includes("HTTP Public Key Pinning: true"), - "Correct HPKP value found"); - let certChain = getCertChain(message.securityInfoAsString); - ok(message.text.includes(certChain), "Found certificate chain"); + let errorCode = doc.getElementById("errorCode"); + errorCode.click(); + let div = doc.getElementById("certificateErrorDebugInformation"); + let text = doc.getElementById("certificateErrorText"); - await BrowserTestUtils.removeTab(gBrowser.selectedTab); + let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] + .getService(Ci.nsISerializationHelper); + let serializable = doc.docShell.failedChannel.securityInfo + .QueryInterface(Ci.nsITransportSecurityInfo) + .QueryInterface(Ci.nsISerializable); + let serializedSecurityInfo = serhelper.serializeToString(serializable); + return { + divDisplay: content.getComputedStyle(div).display, + text: text.textContent, + securityInfoAsString: serializedSecurityInfo + }; + }); + isnot(message.divDisplay, "none", "Debug information is visible"); + ok(message.text.includes(badStsUri.spec), "Correct URL found"); + ok(message.text.includes("requested domain name does not match the server\u2019s certificate"), + "Correct error message found"); + ok(message.text.includes("HTTP Strict Transport Security: false"), + "Correct HSTS value found"); + ok(message.text.includes("HTTP Public Key Pinning: true"), + "Correct HPKP value found"); + let certChain = getCertChain(message.securityInfoAsString); + ok(message.text.includes(certChain), "Found certificate chain"); + + await BrowserTestUtils.removeTab(gBrowser.selectedTab); + } }); add_task(async function checkUnknownIssuerLearnMoreLink() { info("Loading a cert error for self-signed pages and checking the correct link is shown"); - let browser; - let certErrorLoaded; - await BrowserTestUtils.openNewForegroundTab(gBrowser, () => { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, UNKNOWN_ISSUER); - browser = gBrowser.selectedBrowser; - certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); - }, false); + for (let useFrame of [false, true]) { + let tab = await openErrorPage(UNKNOWN_ISSUER, useFrame); + let browser = tab.linkedBrowser; - info("Loading and waiting for the cert error"); - await certErrorLoaded; + let href = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) { + let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document; + let learnMoreLink = doc.getElementById("learnMoreLink"); + return learnMoreLink.href; + }); + ok(href.endsWith("security-error"), "security-error in the Learn More URL"); - let href = await ContentTask.spawn(browser, null, async function() { - let learnMoreLink = content.document.getElementById("learnMoreLink"); - return learnMoreLink.href; - }); - ok(href.endsWith("security-error"), "security-error in the Learn More URL"); - - await BrowserTestUtils.removeTab(gBrowser.selectedTab); + await BrowserTestUtils.removeTab(gBrowser.selectedTab); + } }); function getCertChain(securityInfoAsString) { diff --git a/browser/base/content/test/about/dummy_page.html b/browser/base/content/test/about/dummy_page.html new file mode 100644 index 000000000000..1a87e28408d0 --- /dev/null +++ b/browser/base/content/test/about/dummy_page.html @@ -0,0 +1,9 @@ + + +Dummy test page + + + +

Dummy test page

+ + diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 7ad6e22aaf52..33f1c205c376 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -1212,6 +1212,10 @@ BrowserGlue.prototype = { Services.tm.idleDispatchToMainThread(() => { LanguagePrompt.init(); }); + + Services.tm.idleDispatchToMainThread(() => { + Services.blocklist.loadBlocklistAsync(); + }); }, /** diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index dee998cbe7eb..88de8a64f0fc 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -3547,7 +3547,7 @@ var SessionStoreInternal = { * a tab to speculatively connect on mouse hover. */ speculativeConnectOnTabHover(tab) { - if (this._restore_on_demand && !tab.__SS_connectionPrepared && tab.hasAttribute("pending")) { + if (tab.__SS_lazyData && !tab.__SS_connectionPrepared) { let url = this.getLazyTabValue(tab, "url"); let prepared = this.prepareConnectionToHost(url); // This is used to test if a connection has been made beforehand. diff --git a/devtools/client/netmonitor/src/components/RequestListContent.js b/devtools/client/netmonitor/src/components/RequestListContent.js index 74858936c3b8..2b4dd0a1ad0a 100644 --- a/devtools/client/netmonitor/src/components/RequestListContent.js +++ b/devtools/client/netmonitor/src/components/RequestListContent.js @@ -15,7 +15,6 @@ const { formDataURI } = require("../utils/request-utils"); const { getDisplayedRequests, getSelectedRequest, - getSortedRequests, getWaterfallScale, } = require("../selectors/index"); @@ -65,7 +64,6 @@ class RequestListContent extends Component { openStatistics: PropTypes.func.isRequired, scale: PropTypes.number, selectedRequest: PropTypes.object, - sortedRequests: PropTypes.array.isRequired, requestFilterTypes: PropTypes.object.isRequired, }; } @@ -245,8 +243,8 @@ class RequestListContent extends Component { onContextMenu(evt) { evt.preventDefault(); - let { selectedRequest, sortedRequests } = this.props; - this.contextMenu.open(evt, selectedRequest, sortedRequests); + let { selectedRequest, displayedRequests } = this.props; + this.contextMenu.open(evt, selectedRequest, displayedRequests); } /** @@ -317,7 +315,6 @@ module.exports = connect( firstRequestStartedMillis: state.requests.firstStartedMillis, selectedRequest: getSelectedRequest(state), scale: getWaterfallScale(state), - sortedRequests: getSortedRequests(state), requestFilterTypes: state.filters.requestFilterTypes, }), (dispatch, props) => ({ diff --git a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js index 85191c5f2b46..c6817db217fe 100644 --- a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js +++ b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js @@ -26,7 +26,7 @@ class RequestListContextMenu { this.props = props; } - open(event, selectedRequest, sortedRequests) { + open(event, selectedRequest, requests) { let { id, isCustom, @@ -143,8 +143,8 @@ class RequestListContextMenu { id: "request-list-context-copy-all-as-har", label: L10N.getStr("netmonitor.context.copyAllAsHar"), accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"), - visible: sortedRequests.size > 0, - click: () => this.copyAllAsHar(sortedRequests), + visible: requests.size > 0, + click: () => this.copyAllAsHar(requests), }); menu.push({ @@ -158,8 +158,8 @@ class RequestListContextMenu { id: "request-list-context-save-all-as-har", label: L10N.getStr("netmonitor.context.saveAllAsHar"), accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"), - visible: sortedRequests.size > 0, - click: () => this.saveAllAsHar(sortedRequests), + visible: requests.size > 0, + click: () => this.saveAllAsHar(requests), }); menu.push({ @@ -219,7 +219,7 @@ class RequestListContextMenu { id: "request-list-context-perf", label: L10N.getStr("netmonitor.context.perfTools"), accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"), - visible: sortedRequests.size > 0, + visible: requests.size > 0, click: () => openStatistics(true), }); @@ -393,25 +393,25 @@ class RequestListContextMenu { /** * Copy HAR from the network panel content to the clipboard. */ - copyAllAsHar(sortedRequests) { - return HarExporter.copy(this.getDefaultHarOptions(sortedRequests)); + copyAllAsHar(requests) { + return HarExporter.copy(this.getDefaultHarOptions(requests)); } /** * Save HAR from the network panel content to a file. */ - saveAllAsHar(sortedRequests) { + saveAllAsHar(requests) { // This will not work in launchpad // document.execCommand(‘cut’/‘copy’) was denied because it was not called from // inside a short running user-generated event handler. // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard - return HarExporter.save(this.getDefaultHarOptions(sortedRequests)); + return HarExporter.save(this.getDefaultHarOptions(requests)); } - getDefaultHarOptions(sortedRequests) { + getDefaultHarOptions(requests) { return { connector: this.props.connector, - items: sortedRequests, + items: requests, }; } } diff --git a/devtools/server/actors/object.js b/devtools/server/actors/object.js index 7bb4b2778356..a606f82bdbbe 100644 --- a/devtools/server/actors/object.js +++ b/devtools/server/actors/object.js @@ -1771,8 +1771,8 @@ DebuggerServer.ObjectActorPreviewers.Object = [ obj.class != "StyleSheetList" && obj.class != "CSSValueList" && obj.class != "NamedNodeMap" && - !(rawObj instanceof Ci.nsIDOMFileList || - rawObj instanceof Ci.nsIDOMNodeList)) { + obj.class != "FileList" && + obj.class != "NodeList") { return false; } diff --git a/docshell/base/nsDocShellTreeOwner.cpp b/docshell/base/nsDocShellTreeOwner.cpp index 99233ef23992..2b4938925084 100644 --- a/docshell/base/nsDocShellTreeOwner.cpp +++ b/docshell/base/nsDocShellTreeOwner.cpp @@ -32,7 +32,6 @@ #include "mozilla/dom/Element.h" #include "mozilla/dom/SVGTitleElement.h" #include "nsIDOMEvent.h" -#include "nsIDOMFileList.h" #include "nsIDOMMouseEvent.h" #include "nsIFormControl.h" #include "nsIImageLoadingContent.h" diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 8afba9ca73a8..85755de7468e 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -145,7 +145,6 @@ #include "nsQueryObject.h" #include "nsContentUtils.h" #include "nsCSSProps.h" -#include "nsIDOMFileList.h" #include "nsIURIFixup.h" #ifndef DEBUG #include "nsIAppStartup.h" diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index d53669b94626..36fadc9c9ff6 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -144,7 +144,6 @@ #include "nsQueryObject.h" #include "nsContentUtils.h" #include "nsCSSProps.h" -#include "nsIDOMFileList.h" #include "nsIURIFixup.h" #include "nsIURIMutator.h" #ifndef DEBUG diff --git a/dom/file/FileList.cpp b/dom/file/FileList.cpp index 79aef3e065fb..87617fe158a4 100644 --- a/dom/file/FileList.cpp +++ b/dom/file/FileList.cpp @@ -16,8 +16,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFileList) - NS_INTERFACE_MAP_ENTRY(nsIDOMFileList) + NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(FileList) @@ -29,22 +28,6 @@ FileList::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return mozilla::dom::FileListBinding::Wrap(aCx, this, aGivenProto); } -NS_IMETHODIMP -FileList::GetLength(uint32_t* aLength) -{ - *aLength = Length(); - - return NS_OK; -} - -NS_IMETHODIMP -FileList::Item(uint32_t aIndex, nsISupports** aValue) -{ - nsCOMPtr file = Item(aIndex); - file.forget(aValue); - return NS_OK; -} - File* FileList::Item(uint32_t aIndex) const { diff --git a/dom/file/FileList.h b/dom/file/FileList.h index 6539f878d7f3..0c39dd64e4cd 100644 --- a/dom/file/FileList.h +++ b/dom/file/FileList.h @@ -10,7 +10,6 @@ #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/UnionTypes.h" #include "nsCycleCollectionParticipant.h" -#include "nsIDOMFileList.h" #include "nsWrapperCache.h" namespace mozilla { @@ -19,15 +18,13 @@ namespace dom { class BlobImpls; class File; -class FileList final : public nsIDOMFileList, +class FileList final : public nsISupports, public nsWrapperCache { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileList) - NS_DECL_NSIDOMFILELIST - explicit FileList(nsISupports* aParent) : mParent(aParent) {} diff --git a/dom/file/moz.build b/dom/file/moz.build index 2596b378de33..02c078d1fa37 100644 --- a/dom/file/moz.build +++ b/dom/file/moz.build @@ -11,7 +11,6 @@ DIRS += ['ipc'] XPIDL_SOURCES += [ 'nsIDOMBlob.idl', - 'nsIDOMFileList.idl', ] XPIDL_MODULE = 'dom_file' diff --git a/dom/file/nsIDOMFileList.idl b/dom/file/nsIDOMFileList.idl deleted file mode 100644 index 8e6dcf52a3d8..000000000000 --- a/dom/file/nsIDOMFileList.idl +++ /dev/null @@ -1,14 +0,0 @@ -/* -*- Mode: IDL; 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 "nsISupports.idl" - -[builtinclass, uuid(57128a85-34de-42db-a252-84dd57724a59)] -interface nsIDOMFileList : nsISupports -{ - readonly attribute unsigned long length; - // returns a DOM File object - nsISupports item(in unsigned long index); -}; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index d62b5f1e8c27..042dfa822ffa 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -2755,12 +2755,11 @@ HTMLInputElement::SetFilesOrDirectories(const nsTArray& a } void -HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, +HTMLInputElement::SetFiles(FileList* aFiles, bool aSetValueChanged) { MOZ_ASSERT(mFileData); - RefPtr files = static_cast(aFiles); mFileData->mFilesOrDirectories.Clear(); mFileData->ClearGetFilesHelpers(); @@ -2770,12 +2769,11 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, } if (aFiles) { - uint32_t listLength; - aFiles->GetLength(&listLength); + uint32_t listLength = aFiles->Length(); for (uint32_t i = 0; i < listLength; i++) { OwningFileOrDirectory* element = mFileData->mFilesOrDirectories.AppendElement(); - element->SetAsFile() = files->Item(i); + element->SetAsFile() = aFiles->Item(i); } } diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 753f4b920a87..1321bfd25df6 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -274,7 +274,7 @@ public: void SetFilesOrDirectories(const nsTArray& aFilesOrDirectories, bool aSetValueChanged); - void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged); + void SetFiles(FileList* aFiles, bool aSetValueChanged); // This method is used for test only. Onces the data is set, a 'change' event // is dispatched. diff --git a/dom/interfaces/html/nsIDOMHTMLInputElement.idl b/dom/interfaces/html/nsIDOMHTMLInputElement.idl index b79806b7a210..fc6a1b3814f7 100644 --- a/dom/interfaces/html/nsIDOMHTMLInputElement.idl +++ b/dom/interfaces/html/nsIDOMHTMLInputElement.idl @@ -5,9 +5,6 @@ #include "nsISupports.idl" -interface nsIControllers; -interface nsIDOMFileList; - /** * The nsIDOMHTMLInputElement interface is the interface to a [X]HTML * input element. diff --git a/dom/messagechannel/MessagePort.cpp b/dom/messagechannel/MessagePort.cpp index 22f6abe06e57..e8cbf997c668 100644 --- a/dom/messagechannel/MessagePort.cpp +++ b/dom/messagechannel/MessagePort.cpp @@ -33,7 +33,6 @@ #include "nsIBFCacheEntry.h" #include "nsIDocument.h" -#include "nsIDOMFileList.h" #include "nsIPresShell.h" #include "nsISupportsPrimitives.h" #include "nsServiceManagerUtils.h" diff --git a/js/xpconnect/src/ExportHelpers.cpp b/js/xpconnect/src/ExportHelpers.cpp index 4cda826c6834..8a22a7264bd6 100644 --- a/js/xpconnect/src/ExportHelpers.cpp +++ b/js/xpconnect/src/ExportHelpers.cpp @@ -17,7 +17,6 @@ #include "mozilla/dom/StructuredCloneHolder.h" #include "nsGlobalWindow.h" #include "nsJSUtils.h" -#include "nsIDOMFileList.h" using namespace mozilla; using namespace mozilla::dom; diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 7331bf51f20e..b3c82936f1da 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -31,7 +31,6 @@ #include "mozilla/dom/WindowBinding.h" #include "mozilla/Scheduler.h" #include "nsZipArchive.h" -#include "nsIDOMFileList.h" #include "nsWindowMemoryReporter.h" #include "nsDOMClassInfo.h" #include "ShimInterfaceInfo.h" diff --git a/layout/forms/nsFileControlFrame.cpp b/layout/forms/nsFileControlFrame.cpp index f15063b07549..837ada92d915 100644 --- a/layout/forms/nsFileControlFrame.cpp +++ b/layout/forms/nsFileControlFrame.cpp @@ -25,7 +25,6 @@ #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "mozilla/EventStates.h" -#include "nsIDOMFileList.h" #include "nsTextNode.h" using namespace mozilla; @@ -342,7 +341,7 @@ nsFileControlFrame::DnDListener::HandleEvent(nsIDOMEvent* aEvent) } nsresult -nsFileControlFrame::DnDListener::GetBlobImplForWebkitDirectory(nsIDOMFileList* aFileList, +nsFileControlFrame::DnDListener::GetBlobImplForWebkitDirectory(FileList* aFileList, BlobImpl** aBlobImpl) { *aBlobImpl = nullptr; @@ -360,12 +359,11 @@ nsFileControlFrame::DnDListener::GetBlobImplForWebkitDirectory(nsIDOMFileList* a return NS_ERROR_FAILURE; } - FileList* files = static_cast(aFileList); // webkitdirectory doesn't care about the length of the file list but // only about the first item on it. - uint32_t len = files->Length(); + uint32_t len = aFileList->Length(); if (len) { - File* file = files->Item(0); + File* file = aFileList->Item(0); if (file) { BlobImpl* impl = file->Impl(); if (impl && impl->IsDirectory()) { @@ -406,7 +404,7 @@ nsFileControlFrame::DnDListener::CanDropTheseFiles(DataTransfer* aDataTransfer, uint32_t listLength = 0; if (fileList) { - fileList->GetLength(&listLength); + listLength = fileList->Length(); } return listLength <= 1 || aSupportsMultiple; } diff --git a/layout/forms/nsFileControlFrame.h b/layout/forms/nsFileControlFrame.h index 243decd01d8e..b0bfb495d70b 100644 --- a/layout/forms/nsFileControlFrame.h +++ b/layout/forms/nsFileControlFrame.h @@ -14,9 +14,9 @@ #include "nsIAnonymousContentCreator.h" #include "nsCOMPtr.h" -class nsIDOMFileList; namespace mozilla { namespace dom { +class FileList; class BlobImpl; class DataTransfer; } // namespace dom @@ -119,7 +119,7 @@ protected: NS_DECL_NSIDOMEVENTLISTENER - nsresult GetBlobImplForWebkitDirectory(nsIDOMFileList* aFileList, + nsresult GetBlobImplForWebkitDirectory(mozilla::dom::FileList* aFileList, mozilla::dom::BlobImpl** aBlobImpl); bool IsValidDropData(mozilla::dom::DataTransfer* aDataTransfer); diff --git a/layout/tools/reftest/reftest-content.js b/layout/tools/reftest/reftest-content.js index 28cce1d57b0c..e21383bf5b4a 100644 --- a/layout/tools/reftest/reftest-content.js +++ b/layout/tools/reftest/reftest-content.js @@ -582,7 +582,11 @@ function WaitForTestEnd(contentRootElement, inPrintMode, spellCheckedElements) { return; } - FlushRendering(); + // We don't need to flush styles any more when we are in the state + // after reftest-wait has removed. + if (state != STATE_WAITING_TO_FINISH) { + FlushRendering(); + } switch (state) { case STATE_WAITING_TO_FIRE_INVALIDATE_EVENT: { diff --git a/python/mozlint/mozlint/cli.py b/python/mozlint/mozlint/cli.py index 1f0c3ab8d835..07c10adcf914 100644 --- a/python/mozlint/mozlint/cli.py +++ b/python/mozlint/mozlint/cli.py @@ -175,7 +175,7 @@ def run(paths, linters, fmt, outgoing, workdir, edit, # Encode output with 'replace' to avoid UnicodeEncodeErrors on # environments that aren't using utf-8. - out = formatter(results, failed=lint.failed).encode( + out = formatter(results, failed=lint.failed | lint.failed_setup).encode( sys.stdout.encoding or 'ascii', 'replace') if out: print(out) diff --git a/python/mozlint/mozlint/roller.py b/python/mozlint/mozlint/roller.py index 23a10383e012..402cae260743 100644 --- a/python/mozlint/mozlint/roller.py +++ b/python/mozlint/mozlint/roller.py @@ -12,6 +12,7 @@ from collections import defaultdict from concurrent.futures import ProcessPoolExecutor from math import ceil from multiprocessing import cpu_count +from multiprocessing.queues import Queue from subprocess import CalledProcessError from mozversioncontrol import get_repository_object, MissingUpstreamRepo, InvalidRepoPath @@ -21,13 +22,27 @@ from .parser import Parser from .pathutils import findobject from .types import supported_types +SHUTDOWN = False +orig_sigint = signal.getsignal(signal.SIGINT) -def _run_linters(config, paths, **lintargs): + +def _run_worker(config, paths, **lintargs): results = defaultdict(list) failed = [] + if SHUTDOWN: + return results, failed + func = supported_types[config['type']] - res = func(paths, config, **lintargs) or [] + try: + res = func(paths, config, **lintargs) or [] + except Exception: + traceback.print_exc() + res = 1 + except (KeyboardInterrupt, SystemExit): + return results, failed + finally: + sys.stdout.flush() if not isinstance(res, (list, tuple)): if res: @@ -38,16 +53,30 @@ def _run_linters(config, paths, **lintargs): return results, failed -def _run_worker(*args, **kwargs): - try: - return _run_linters(*args, **kwargs) - except Exception: - # multiprocessing seems to munge worker exceptions, print - # it here so it isn't lost. - traceback.print_exc() - raise - finally: - sys.stdout.flush() +class InterruptableQueue(Queue): + """A multiprocessing.Queue that catches KeyboardInterrupt when a worker is + blocking on it and returns None. + + This is needed to gracefully handle KeyboardInterrupts when a worker is + blocking on ProcessPoolExecutor's call queue. + """ + + def get(self, *args, **kwargs): + try: + return Queue.get(self, *args, **kwargs) + except KeyboardInterrupt: + return None + + +def _worker_sigint_handler(signum, frame): + """Sigint handler for the worker subprocesses. + + Tells workers not to process the extra jobs on the call queue that couldn't + be canceled by the parent process. + """ + global SHUTDOWN + SHUTDOWN = True + orig_sigint(signum, frame) class LintRoller(object): @@ -71,8 +100,11 @@ class LintRoller(object): self.lintargs = lintargs self.lintargs['root'] = root - # linters that return non-zero - self.failed = set() + # result state + self.failed = None + self.failed_setup = None + self.results = None + self.root = root def read(self, paths): @@ -91,7 +123,7 @@ class LintRoller(object): if not self.linters: raise LintersNotConfigured - failed = set() + self.failed_setup = set() for linter in self.linters: if 'setup' not in linter: continue @@ -103,12 +135,12 @@ class LintRoller(object): res = 1 if res: - failed.add(linter['name']) + self.failed_setup.add(linter['name']) - if failed: - print("error: problem with lint setup, skipping {}".format(', '.join(sorted(failed)))) - self.linters = [l for l in self.linters if l['name'] not in failed] - self.failed.update(failed) + if self.failed_setup: + print("error: problem with lint setup, skipping {}".format( + ', '.join(sorted(self.failed_setup)))) + self.linters = [l for l in self.linters if l['name'] not in self.failed_setup] return 1 return 0 @@ -120,6 +152,16 @@ class LintRoller(object): yield linter, paths[:chunk_size] paths = paths[chunk_size:] + def _collect_results(self, future): + if future.cancelled(): + return + + results, failed = future.result() + if failed: + self.failed.update(set(failed)) + for k, v in results.iteritems(): + self.results[k].extend(v) + def roll(self, paths=None, outgoing=None, workdir=None, num_procs=None): """Run all of the registered linters against the specified file paths. @@ -133,6 +175,10 @@ class LintRoller(object): if not self.linters: raise LintersNotConfigured + # reset result state + self.results = defaultdict(list) + self.failed = set() + # Need to use a set in case vcs operations specify the same file # more than once. paths = paths or set() @@ -170,19 +216,38 @@ class LintRoller(object): paths = map(os.path.abspath, paths) num_procs = num_procs or cpu_count() - all_results = defaultdict(list) - with ProcessPoolExecutor(num_procs) as executor: - futures = [executor.submit(_run_worker, config, p, **self.lintargs) - for config, p in self._generate_jobs(paths, num_procs)] - # ignore SIGINT in parent so we can still get partial results - # from child processes. These should shutdown quickly anyway. - orig_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN) - for future in futures: - results, failed = future.result() - if failed: - self.failed.update(set(failed)) - for k, v in results.iteritems(): - all_results[k].extend(v) + jobs = list(self._generate_jobs(paths, num_procs)) + # Make sure we never spawn more processes than we have jobs. + num_procs = min(len(jobs), num_procs) + + signal.signal(signal.SIGINT, _worker_sigint_handler) + executor = ProcessPoolExecutor(num_procs) + executor._call_queue = InterruptableQueue(executor._call_queue._maxsize) + + # Submit jobs to the worker pool. The _collect_results method will be + # called when a job is finished. We store the futures so that they can + # be canceled in the event of a KeyboardInterrupt. + futures = [] + for job in jobs: + future = executor.submit(_run_worker, *job, **self.lintargs) + future.add_done_callback(self._collect_results) + futures.append(future) + + def _parent_sigint_handler(signum, frame): + """Sigint handler for the parent process. + + Cancels all jobs that have not yet been placed on the call queue. + The parent process won't exit until all workers have terminated. + Assuming the linters are implemented properly, this shouldn't take + more than a couple seconds. + """ + [f.cancel() for f in futures] + executor.shutdown(wait=False) + print("\nwarning: not all files were linted") + signal.signal(signal.SIGINT, signal.SIG_IGN) + + signal.signal(signal.SIGINT, _parent_sigint_handler) + executor.shutdown() signal.signal(signal.SIGINT, orig_sigint) - return all_results + return self.results diff --git a/python/mozlint/test/linters/external.py b/python/mozlint/test/linters/external.py index 806a5fa1cfe8..3d3a68c387d4 100644 --- a/python/mozlint/test/linters/external.py +++ b/python/mozlint/test/linters/external.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, print_function import os +import time from mozlint import result from mozlint.errors import LintException @@ -36,6 +37,11 @@ def raises(files, config, **lintargs): raise LintException("Oh no something bad happened!") +def slow(files, config, **lintargs): + time.sleep(2) + return [] + + def structured(files, config, logger, **kwargs): for path in files: if os.path.isdir(path): diff --git a/python/mozlint/test/linters/slow.yml b/python/mozlint/test/linters/slow.yml new file mode 100644 index 000000000000..2c476790108c --- /dev/null +++ b/python/mozlint/test/linters/slow.yml @@ -0,0 +1,8 @@ +--- +SlowLinter: + description: A linter that takes awhile to run + include: + - files + type: external + extensions: ['.js', '.jsm'] + payload: external:slow diff --git a/python/mozlint/test/runcli.py b/python/mozlint/test/runcli.py new file mode 100644 index 000000000000..54be88b845d7 --- /dev/null +++ b/python/mozlint/test/runcli.py @@ -0,0 +1,20 @@ +# 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/. + +from __future__ import absolute_import + +import os +import sys + +here = os.path.abspath(os.path.dirname(__file__)) +sys.path.insert(0, os.path.join(os.path.dirname(here), 'mozlint')) + +from mozlint import cli +cli.SEARCH_PATHS.append(os.path.join(here, 'linters')) + +if __name__ == '__main__': + parser = cli.MozlintParser() + args = vars(parser.parse_args(sys.argv[1:])) + args['root'] = here + sys.exit(cli.run(**args)) diff --git a/python/mozlint/test/test_cli.py b/python/mozlint/test/test_cli.py index 7e9f672a3e21..573e650a0af2 100644 --- a/python/mozlint/test/test_cli.py +++ b/python/mozlint/test/test_cli.py @@ -49,10 +49,11 @@ def test_cli_run_with_edit(run, parser, capfd): out, err = capfd.readouterr() out = out.splitlines() assert ret == 1 - assert len(out) == 5 assert out[0].endswith('foobar.js') # from the `echo` editor assert "foobar.js: line 1, col 1, Error" in out[1] assert "foobar.js: line 2, col 1, Error" in out[2] + assert "2 problems" in out[-1] + assert len(out) == 5 del os.environ['EDITOR'] with pytest.raises(SystemExit): diff --git a/python/mozlint/test/test_roller.py b/python/mozlint/test/test_roller.py index a26598ab449f..7b9825dc156a 100644 --- a/python/mozlint/test/test_roller.py +++ b/python/mozlint/test/test_roller.py @@ -6,13 +6,16 @@ from __future__ import absolute_import import os import platform +import signal +import subprocess import sys +import time import mozunit import pytest from mozlint import ResultContainer -from mozlint.errors import LintersNotConfigured, LintException +from mozlint.errors import LintersNotConfigured here = os.path.abspath(os.path.dirname(__file__)) @@ -29,8 +32,10 @@ def test_roll_no_linters_configured(lint, files): def test_roll_successful(lint, linters, files): lint.read(linters) + assert lint.results is None result = lint.roll(files) assert len(result) == 1 + assert lint.results == result assert lint.failed == set([]) path = result.keys()[0] @@ -45,15 +50,12 @@ def test_roll_successful(lint, linters, files): assert container.rule == 'no-foobar' -def test_roll_catch_exception(lint, lintdir, files): +def test_roll_catch_exception(lint, lintdir, files, capfd): lint.read(os.path.join(lintdir, 'raises.yml')) - # suppress printed traceback from test output - old_stderr = sys.stderr - sys.stderr = open(os.devnull, 'w') - with pytest.raises(LintException): - lint.roll(files) - sys.stderr = old_stderr + lint.roll(files) # assert not raises + out, err = capfd.readouterr() + assert 'LintException' in err def test_roll_with_excluded_path(lint, linters, files): @@ -76,13 +78,13 @@ def test_roll_with_invalid_extension(lint, lintdir, filedir): def test_roll_with_failure_code(lint, lintdir, files): lint.read(os.path.join(lintdir, 'badreturncode.yml')) - assert lint.failed == set([]) + assert lint.failed is None result = lint.roll(files, num_procs=1) assert len(result) == 0 assert lint.failed == set(['BadReturnCodeLinter']) -def fake_run_linters(config, paths, **lintargs): +def fake_run_worker(config, paths, **lintargs): return {'count': [1]}, [] @@ -90,7 +92,7 @@ def fake_run_linters(config, paths, **lintargs): reason="monkeypatch issues with multiprocessing on Windows") @pytest.mark.parametrize('num_procs', [1, 4, 8, 16]) def test_number_of_jobs(monkeypatch, lint, linters, files, num_procs): - monkeypatch.setattr(sys.modules[lint.__module__], '_run_linters', fake_run_linters) + monkeypatch.setattr(sys.modules[lint.__module__], '_run_worker', fake_run_worker) lint.read(linters) num_jobs = len(lint.roll(files, num_procs=num_procs)['count']) @@ -105,7 +107,7 @@ def test_number_of_jobs(monkeypatch, lint, linters, files, num_procs): reason="monkeypatch issues with multiprocessing on Windows") @pytest.mark.parametrize('max_paths,expected_jobs', [(1, 12), (4, 6), (16, 6)]) def test_max_paths_per_job(monkeypatch, lint, linters, files, max_paths, expected_jobs): - monkeypatch.setattr(sys.modules[lint.__module__], '_run_linters', fake_run_linters) + monkeypatch.setattr(sys.modules[lint.__module__], '_run_worker', fake_run_worker) files = files[:4] assert len(files) == 4 @@ -119,6 +121,23 @@ def test_max_paths_per_job(monkeypatch, lint, linters, files, max_paths, expecte assert num_jobs == expected_jobs +@pytest.mark.skipif(platform.system() == 'Windows', + reason="signal.CTRL_C_EVENT isn't causing a KeyboardInterrupt on Windows") +def test_keyboard_interrupt(): + # We use two linters so we'll have two jobs. One (string.yml) will complete + # quickly. The other (slow.yml) will run slowly. This way the first worker + # will be be stuck blocking on the ProcessPoolExecutor._call_queue when the + # signal arrives and the other still be doing work. + cmd = [sys.executable, 'runcli.py', '-l=string', '-l=slow'] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=here) + time.sleep(1) + proc.send_signal(signal.SIGINT) + + out = proc.communicate()[0] + assert 'warning: not all files were linted' in out + assert 'Traceback' not in out + + linters = ('setup.yml', 'setupfailed.yml', 'setupraised.yml') @@ -133,7 +152,7 @@ def test_setup(lint, linters, filedir, capfd): assert 'setup failed' in out assert 'setup raised' in out assert 'error: problem with lint setup, skipping' in out - assert lint.failed == set(['SetupFailedLinter', 'SetupRaisedLinter']) + assert lint.failed_setup == set(['SetupFailedLinter', 'SetupRaisedLinter']) if __name__ == '__main__': diff --git a/servo/Cargo.lock b/servo/Cargo.lock index ae1ca42f20e9..1270490786ff 100644 --- a/servo/Cargo.lock +++ b/servo/Cargo.lock @@ -2815,6 +2815,7 @@ dependencies = [ "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.0.1", + "style_traits 0.0.1", "webrender_api 0.57.0 (git+https://github.com/servo/webrender)", ] diff --git a/servo/components/compositing/compositor.rs b/servo/components/compositing/compositor.rs index 88a3dc6b5eff..2b409b7ae3d4 100644 --- a/servo/components/compositing/compositor.rs +++ b/servo/components/compositing/compositor.rs @@ -21,7 +21,7 @@ use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, To use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType}; use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent}; use servo_config::opts; -use servo_geometry::DeviceIndependentPixel; +use servo_geometry::{DeviceIndependentPixel, DeviceUintLength}; use std::collections::HashMap; use std::env; use std::fs::File; @@ -34,7 +34,7 @@ use style_traits::viewport::ViewportConstraints; use time::{now, precise_time_ns, precise_time_s}; use touch::{TouchHandler, TouchAction}; use webrender; -use webrender_api::{self, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult}; +use webrender_api::{self, DeviceIntPoint, DevicePoint, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult}; use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation}; use windowing::{self, MouseWindowEvent, WebRenderDebugOption, WindowMethods}; @@ -202,7 +202,7 @@ struct ScrollZoomEvent { /// Scroll by this offset, or to Start or End scroll_location: ScrollLocation, /// Apply changes to the frame at this location - cursor: TypedPoint2D, + cursor: DeviceIntPoint, /// The scroll event phase. phase: ScrollEventPhase, /// The number of OS events that have been coalesced together into this one event. @@ -275,15 +275,15 @@ impl RenderTargetInfo { } } -fn initialize_png(gl: &gl::Gl, width: usize, height: usize) -> RenderTargetInfo { +fn initialize_png(gl: &gl::Gl, width: DeviceUintLength, height: DeviceUintLength) -> RenderTargetInfo { let framebuffer_ids = gl.gen_framebuffers(1); gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]); let texture_ids = gl.gen_textures(1); gl.bind_texture(gl::TEXTURE_2D, texture_ids[0]); - gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as gl::GLint, width as gl::GLsizei, - height as gl::GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None); + gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as gl::GLint, width.get() as gl::GLsizei, + height.get() as gl::GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None); gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as gl::GLint); gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as gl::GLint); @@ -297,8 +297,8 @@ fn initialize_png(gl: &gl::Gl, width: usize, height: usize) -> RenderTargetInfo gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); gl.renderbuffer_storage(gl::RENDERBUFFER, gl::DEPTH_COMPONENT24, - width as gl::GLsizei, - height as gl::GLsizei); + width.get() as gl::GLsizei, + height.get() as gl::GLsizei); gl.framebuffer_renderbuffer(gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT, gl::RENDERBUFFER, @@ -647,9 +647,11 @@ impl IOCompositor { device_pixel_ratio: dppx, initial_viewport: initial_viewport, }; + let top_level_browsing_context_id = self.root_pipeline.as_ref().map(|pipeline| { pipeline.top_level_browsing_context_id }); + let msg = ConstellationMsg::WindowSize(top_level_browsing_context_id, data, size_type); if let Err(e) = self.constellation_chan.send(msg) { @@ -728,7 +730,7 @@ impl IOCompositor { } } - fn hit_test_at_point(&self, point: TypedPoint2D) -> HitTestResult { + fn hit_test_at_point(&self, point: DevicePoint) -> HitTestResult { let dppx = self.page_zoom * self.hidpi_factor(); let scaled_point = (point / dppx).to_untyped(); @@ -742,7 +744,7 @@ impl IOCompositor { } - pub fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D) { + pub fn on_mouse_window_move_event_class(&mut self, cursor: DevicePoint) { if opts::get().convert_mouse_to_touch { self.on_touch_move(TouchId(0), cursor); return @@ -751,7 +753,7 @@ impl IOCompositor { self.dispatch_mouse_window_move_event_class(cursor); } - fn dispatch_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D) { + fn dispatch_mouse_window_move_event_class(&mut self, cursor: DevicePoint) { let root_pipeline_id = match self.get_root_pipeline_id() { Some(root_pipeline_id) => root_pipeline_id, None => return, @@ -783,7 +785,7 @@ impl IOCompositor { &self, event_type: TouchEventType, identifier: TouchId, - point: TypedPoint2D) + point: DevicePoint) { let results = self.hit_test_at_point(point); if let Some(item) = results.items.first() { @@ -804,7 +806,7 @@ impl IOCompositor { pub fn on_touch_event(&mut self, event_type: TouchEventType, identifier: TouchId, - location: TypedPoint2D) { + location: DevicePoint) { match event_type { TouchEventType::Down => self.on_touch_down(identifier, location), TouchEventType::Move => self.on_touch_move(identifier, location), @@ -813,12 +815,12 @@ impl IOCompositor { } } - fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D) { + fn on_touch_down(&mut self, identifier: TouchId, point: DevicePoint) { self.touch_handler.on_touch_down(identifier, point); self.send_touch_event(TouchEventType::Down, identifier, point); } - fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D) { + fn on_touch_move(&mut self, identifier: TouchId, point: DevicePoint) { match self.touch_handler.on_touch_move(identifier, point) { TouchAction::Scroll(delta) => { match point.cast() { @@ -849,7 +851,7 @@ impl IOCompositor { } } - fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D) { + fn on_touch_up(&mut self, identifier: TouchId, point: DevicePoint) { self.send_touch_event(TouchEventType::Up, identifier, point); if let TouchAction::Click = self.touch_handler.on_touch_up(identifier, point) { @@ -857,14 +859,14 @@ impl IOCompositor { } } - fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D) { + fn on_touch_cancel(&mut self, identifier: TouchId, point: DevicePoint) { // Send the event to script. self.touch_handler.on_touch_cancel(identifier, point); self.send_touch_event(TouchEventType::Cancel, identifier, point); } /// - fn simulate_mouse_click(&mut self, p: TypedPoint2D) { + fn simulate_mouse_click(&mut self, p: DevicePoint) { let button = MouseButton::Left; self.dispatch_mouse_window_move_event_class(p); self.dispatch_mouse_window_event_class(MouseWindowEvent::MouseDown(button, p)); @@ -874,7 +876,7 @@ impl IOCompositor { pub fn on_scroll_event(&mut self, delta: ScrollLocation, - cursor: TypedPoint2D, + cursor: DeviceIntPoint, phase: TouchEventType) { match phase { TouchEventType::Move => self.on_scroll_window_event(delta, cursor), @@ -889,7 +891,7 @@ impl IOCompositor { fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, - cursor: TypedPoint2D) { + cursor: DeviceIntPoint) { let event_phase = match (self.scroll_in_progress, self.in_scroll_transaction) { (false, None) => ScrollEventPhase::Start, (false, Some(last_scroll)) if last_scroll.elapsed() > Duration::from_millis(80) => @@ -908,7 +910,7 @@ impl IOCompositor { fn on_scroll_start_window_event(&mut self, scroll_location: ScrollLocation, - cursor: TypedPoint2D) { + cursor: DeviceIntPoint) { self.scroll_in_progress = true; self.pending_scroll_zoom_events.push(ScrollZoomEvent { magnification: 1.0, @@ -921,7 +923,7 @@ impl IOCompositor { fn on_scroll_end_window_event(&mut self, scroll_location: ScrollLocation, - cursor: TypedPoint2D) { + cursor: DeviceIntPoint) { self.scroll_in_progress = false; self.pending_scroll_zoom_events.push(ScrollZoomEvent { magnification: 1.0, @@ -1254,8 +1256,7 @@ impl IOCompositor { fn composite_specific_target(&mut self, target: CompositeTarget) -> Result, UnableToComposite> { - let (width, height) = - (self.frame_size.width as usize, self.frame_size.height as usize); + let (width, height) = (self.frame_size.width_typed(), self.frame_size.height_typed()); if !self.window.prepare_for_composite(width, height) { return Err(UnableToComposite::WindowUnprepared) } @@ -1375,9 +1376,11 @@ impl IOCompositor { fn draw_img(&self, render_target_info: RenderTargetInfo, - width: usize, - height: usize) + width: DeviceUintLength, + height: DeviceUintLength) -> RgbImage { + let width = width.get() as usize; + let height = height.get() as usize; // For some reason, OSMesa fails to render on the 3rd // attempt in headless mode, under some conditions. // I think this can only be some kind of synchronization diff --git a/servo/components/compositing/compositor_thread.rs b/servo/components/compositing/compositor_thread.rs index 2695970e3204..d93c2c4a2bd9 100644 --- a/servo/components/compositing/compositor_thread.rs +++ b/servo/components/compositing/compositor_thread.rs @@ -6,7 +6,6 @@ use SendableFrameTree; use compositor::CompositingReason; -use euclid::{Point2D, Size2D}; use gfx_traits::Epoch; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{Key, KeyModifiers, KeyState, PipelineId, TopLevelBrowsingContextId}; @@ -20,7 +19,7 @@ use std::sync::mpsc::{Receiver, Sender}; use style_traits::cursor::CursorKind; use style_traits::viewport::ViewportConstraints; use webrender; -use webrender_api; +use webrender_api::{self, DeviceIntPoint, DeviceUintSize}; /// Used to wake up the event loop, provided by the servo port/embedder. @@ -119,15 +118,16 @@ pub enum EmbedderMsg { /// Alerts the embedder that the current page has changed its title. ChangePageTitle(TopLevelBrowsingContextId, Option), /// Move the window to a point - MoveTo(TopLevelBrowsingContextId, Point2D), + MoveTo(TopLevelBrowsingContextId, DeviceIntPoint), /// Resize the window to size - ResizeTo(TopLevelBrowsingContextId, Size2D), + ResizeTo(TopLevelBrowsingContextId, DeviceUintSize), /// Get Window Informations size and position - GetClientWindow(TopLevelBrowsingContextId, IpcSender<(Size2D, Point2D)>), + GetClientWindow(TopLevelBrowsingContextId, + IpcSender<(DeviceUintSize, DeviceIntPoint)>), /// Get screen size (pixel) - GetScreenSize(TopLevelBrowsingContextId, IpcSender<(Size2D)>), + GetScreenSize(TopLevelBrowsingContextId, IpcSender<(DeviceUintSize)>), /// Get screen available size (pixel) - GetScreenAvailSize(TopLevelBrowsingContextId, IpcSender<(Size2D)>), + GetScreenAvailSize(TopLevelBrowsingContextId, IpcSender<(DeviceUintSize)>), /// Wether or not to follow a link AllowNavigation(TopLevelBrowsingContextId, ServoUrl, IpcSender), /// Sends an unconsumed key event back to the embedder. diff --git a/servo/components/compositing/windowing.rs b/servo/components/compositing/windowing.rs index 0426f0317bf1..a1fb50d46263 100644 --- a/servo/components/compositing/windowing.rs +++ b/servo/components/compositing/windowing.rs @@ -5,26 +5,25 @@ //! Abstract windowing methods. The concrete implementations of these can be found in `platform/`. use compositor_thread::EventLoopWaker; -use euclid::{Point2D, Size2D}; -use euclid::{TypedScale, TypedPoint2D, TypedSize2D}; +use euclid::TypedScale; use gleam::gl; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{Key, KeyModifiers, KeyState, TopLevelBrowsingContextId, TraversalDirection}; use net_traits::net_error_list::NetError; use script_traits::{LoadData, MouseButton, TouchEventType, TouchId}; -use servo_geometry::DeviceIndependentPixel; +use servo_geometry::{DeviceIndependentPixel, DeviceUintLength}; use servo_url::ServoUrl; use std::fmt::{Debug, Error, Formatter}; use std::rc::Rc; use style_traits::DevicePixel; use style_traits::cursor::CursorKind; -use webrender_api::{DeviceUintSize, DeviceUintRect, ScrollLocation}; +use webrender_api::{DeviceIntPoint, DevicePoint, DeviceUintSize, DeviceUintRect, ScrollLocation}; #[derive(Clone)] pub enum MouseWindowEvent { - Click(MouseButton, TypedPoint2D), - MouseDown(MouseButton, TypedPoint2D), - MouseUp(MouseButton, TypedPoint2D), + Click(MouseButton, DevicePoint), + MouseDown(MouseButton, DevicePoint), + MouseUp(MouseButton, DevicePoint), } /// Various debug and profiling flags that WebRender supports. @@ -55,12 +54,12 @@ pub enum WindowEvent { /// Sent when a mouse hit test is to be performed. MouseWindowEventClass(MouseWindowEvent), /// Sent when a mouse move. - MouseWindowMoveEventClass(TypedPoint2D), + MouseWindowMoveEventClass(DevicePoint), /// Touch event: type, identifier, point - Touch(TouchEventType, TouchId, TypedPoint2D), + Touch(TouchEventType, TouchId, DevicePoint), /// Sent when the user scrolls. The first point is the delta and the second point is the /// origin. - Scroll(ScrollLocation, TypedPoint2D, TouchEventType), + Scroll(ScrollLocation, DeviceIntPoint, TouchEventType), /// Sent when the user zooms. Zoom(f32), /// Simulated "pinch zoom" gesture for non-touch platforms (e.g. ctrl-scrollwheel). @@ -126,21 +125,19 @@ pub trait WindowMethods { fn framebuffer_size(&self) -> DeviceUintSize; /// Returns the position and size of the window within the rendering area. fn window_rect(&self) -> DeviceUintRect; - /// Returns the size of the window in density-independent "px" units. - fn size(&self) -> TypedSize2D; /// Presents the window to the screen (perhaps by page flipping). fn present(&self); /// Return the size of the window with head and borders and position of the window values - fn client_window(&self, ctx: TopLevelBrowsingContextId) -> (Size2D, Point2D); - /// Return the size of the screen (pixel) - fn screen_size(&self, ctx: TopLevelBrowsingContextId) -> Size2D; - /// Return the available size of the screen (pixel) - fn screen_avail_size(&self, ctx: TopLevelBrowsingContextId) -> Size2D; + fn client_window(&self, ctx: TopLevelBrowsingContextId) -> (DeviceUintSize, DeviceIntPoint); + /// Return the size of the screen. + fn screen_size(&self, ctx: TopLevelBrowsingContextId) -> DeviceUintSize; + /// Return the available size of the screen. + fn screen_avail_size(&self, ctx: TopLevelBrowsingContextId) -> DeviceUintSize; /// Set the size inside of borders and head - fn set_inner_size(&self, ctx: TopLevelBrowsingContextId, size: Size2D); + fn set_inner_size(&self, ctx: TopLevelBrowsingContextId, size: DeviceUintSize); /// Set the window position - fn set_position(&self, ctx: TopLevelBrowsingContextId, point: Point2D); + fn set_position(&self, ctx: TopLevelBrowsingContextId, point: DeviceIntPoint); /// Set fullscreen state fn set_fullscreen_state(&self, ctx: TopLevelBrowsingContextId, state: bool); @@ -170,7 +167,7 @@ pub trait WindowMethods { /// Requests that the window system prepare a composite. Typically this will involve making /// some type of platform-specific graphics context current. Returns true if the composite may /// proceed and false if it should not. - fn prepare_for_composite(&self, width: usize, height: usize) -> bool; + fn prepare_for_composite(&self, width: DeviceUintLength, height: DeviceUintLength) -> bool; /// Sets the cursor to be used in the window. fn set_cursor(&self, cursor: CursorKind); diff --git a/servo/components/geometry/Cargo.toml b/servo/components/geometry/Cargo.toml index 8ca688bd64cf..d37b8b9e64d4 100644 --- a/servo/components/geometry/Cargo.toml +++ b/servo/components/geometry/Cargo.toml @@ -14,4 +14,5 @@ app_units = "0.6" euclid = "0.17" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = { path = "../malloc_size_of_derive" } +style_traits = { path = "../style_traits" } webrender_api = { git = "https://github.com/servo/webrender" } diff --git a/servo/components/geometry/lib.rs b/servo/components/geometry/lib.rs index c22c9d160556..c4bb9721bf65 100644 --- a/servo/components/geometry/lib.rs +++ b/servo/components/geometry/lib.rs @@ -5,16 +5,20 @@ extern crate app_units; extern crate euclid; extern crate malloc_size_of; +extern crate style_traits; #[macro_use] extern crate malloc_size_of_derive; extern crate webrender_api; use app_units::{Au, MAX_AU, MIN_AU}; -use euclid::{Point2D, Rect, Size2D}; +use euclid::{Length, Point2D, Rect, Size2D}; use std::f32; +use style_traits::DevicePixel; use webrender_api::{LayoutPoint, LayoutRect, LayoutSize}; // Units for use with euclid::length and euclid::scale_factor. +pub type DeviceUintLength = Length; + /// A normalized "pixel" at the default resolution for the display. /// /// Like the CSS "px" unit, the exact physical size of this unit may vary between devices, but it diff --git a/servo/components/script/dom/screen.rs b/servo/components/script/dom/screen.rs index 3cd540349945..e83cffd93a4f 100644 --- a/servo/components/script/dom/screen.rs +++ b/servo/components/script/dom/screen.rs @@ -11,9 +11,11 @@ use dom::bindings::root::{Dom, DomRoot}; use dom::globalscope::GlobalScope; use dom::window::Window; use dom_struct::dom_struct; -use euclid::Size2D; +use euclid::TypedSize2D; use ipc_channel::ipc; use script_traits::ScriptMsg; +use style_traits::CSSPixel; +use webrender_api::DeviceUintSize; #[dom_struct] pub struct Screen { @@ -35,18 +37,22 @@ impl Screen { ScreenBinding::Wrap) } - fn screen_size(&self) -> Size2D { - let (send, recv) = ipc::channel::<(Size2D)>().unwrap(); + fn screen_size(&self) -> TypedSize2D { + let (send, recv) = ipc::channel::().unwrap(); self.window.upcast::() .script_to_constellation_chan().send(ScriptMsg::GetScreenSize(send)).unwrap(); - recv.recv().unwrap_or(Size2D::zero()) + let dpr = self.window.device_pixel_ratio(); + let screen = recv.recv().unwrap_or(TypedSize2D::zero()); + (screen.to_f32() / dpr).to_u32() } - fn screen_avail_size(&self) -> Size2D { - let (send, recv) = ipc::channel::<(Size2D)>().unwrap(); + fn screen_avail_size(&self) -> TypedSize2D { + let (send, recv) = ipc::channel::().unwrap(); self.window.upcast::() .script_to_constellation_chan().send(ScriptMsg::GetScreenAvailSize(send)).unwrap(); - recv.recv().unwrap_or(Size2D::zero()) + let dpr = self.window.device_pixel_ratio(); + let screen = recv.recv().unwrap_or(TypedSize2D::zero()); + (screen.to_f32() / dpr).to_u32() } } diff --git a/servo/components/script/dom/window.rs b/servo/components/script/dom/window.rs index f6177e90fcf0..37f65f774b2e 100644 --- a/servo/components/script/dom/window.rs +++ b/servo/components/script/dom/window.rs @@ -48,7 +48,7 @@ use dom::windowproxy::WindowProxy; use dom::worklet::Worklet; use dom::workletglobalscope::WorkletGlobalScopeType; use dom_struct::dom_struct; -use euclid::{Point2D, Vector2D, Rect, Size2D}; +use euclid::{Point2D, Vector2D, Rect, Size2D, TypedPoint2D, TypedScale, TypedSize2D}; use fetch; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; @@ -102,7 +102,7 @@ use style::properties::{ComputedValues, PropertyId}; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::CssRuleType; -use style_traits::ParsingMode; +use style_traits::{CSSPixel, DevicePixel, ParsingMode}; use task::TaskCanceller; use task_source::dom_manipulation::DOMManipulationTaskSource; use task_source::file_reading::FileReadingTaskSource; @@ -116,7 +116,7 @@ use timers::{IsInterval, TimerCallback}; use tinyfiledialogs::{self, MessageBoxIcon}; use url::Position; use webdriver_handlers::jsval_to_webdriver; -use webrender_api::{ExternalScrollId, DocumentId}; +use webrender_api::{ExternalScrollId, DeviceIntPoint, DeviceUintSize, DocumentId}; use webvr_traits::WebVRMsg; /// Current state of the window object @@ -930,11 +930,12 @@ impl WindowMethods for Window { } // https://drafts.csswg.org/cssom-view/#dom-window-resizeto - fn ResizeTo(&self, x: i32, y: i32) { + fn ResizeTo(&self, width: i32, height: i32) { // Step 1 //TODO determine if this operation is allowed - let size = Size2D::new(x.to_u32().unwrap_or(1), y.to_u32().unwrap_or(1)); - self.send_to_constellation(ScriptMsg::ResizeTo(size)); + let dpr = self.device_pixel_ratio(); + let size = TypedSize2D::new(width, height).to_f32() * dpr; + self.send_to_constellation(ScriptMsg::ResizeTo(size.to_u32())); } // https://drafts.csswg.org/cssom-view/#dom-window-resizeby @@ -948,8 +949,9 @@ impl WindowMethods for Window { fn MoveTo(&self, x: i32, y: i32) { // Step 1 //TODO determine if this operation is allowed - let point = Point2D::new(x, y); - self.send_to_constellation(ScriptMsg::MoveTo(point)); + let dpr = self.device_pixel_ratio(); + let point = TypedPoint2D::new(x, y).to_f32() * dpr; + self.send_to_constellation(ScriptMsg::MoveTo(point.to_i32())); } // https://drafts.csswg.org/cssom-view/#dom-window-moveby @@ -985,8 +987,7 @@ impl WindowMethods for Window { // https://drafts.csswg.org/cssom-view/#dom-window-devicepixelratio fn DevicePixelRatio(&self) -> Finite { - let dpr = self.window_size.get().map_or(1.0f32, |data| data.device_pixel_ratio.get()); - Finite::wrap(dpr as f64) + Finite::wrap(self.device_pixel_ratio().get() as f64) } // https://html.spec.whatwg.org/multipage/#dom-window-status @@ -1174,10 +1175,16 @@ impl Window { self.current_viewport.set(new_viewport) } - pub fn client_window(&self) -> (Size2D, Point2D) { - let (send, recv) = ipc::channel::<(Size2D, Point2D)>().unwrap(); + pub fn device_pixel_ratio(&self) -> TypedScale { + self.window_size.get().map_or(TypedScale::new(1.0), |data| data.device_pixel_ratio) + } + + fn client_window(&self) -> (TypedSize2D, TypedPoint2D) { + let (send, recv) = ipc::channel::<(DeviceUintSize, DeviceIntPoint)>().unwrap(); self.send_to_constellation(ScriptMsg::GetClientWindow(send)); - recv.recv().unwrap_or((Size2D::zero(), Point2D::zero())) + let (size, point) = recv.recv().unwrap_or((TypedSize2D::zero(), TypedPoint2D::zero())); + let dpr = self.device_pixel_ratio(); + ((size.to_f32() / dpr).to_u32(), (point.to_f32() / dpr).to_i32()) } /// Advances the layout animation clock by `delta` milliseconds, and then diff --git a/servo/components/script_traits/lib.rs b/servo/components/script_traits/lib.rs index 46f5ed6c90ce..c65ad10be4b1 100644 --- a/servo/components/script_traits/lib.rs +++ b/servo/components/script_traits/lib.rs @@ -41,7 +41,7 @@ pub mod webdriver_msg; use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; -use euclid::{Size2D, Length, Point2D, Vector2D, Rect, TypedScale, TypedSize2D}; +use euclid::{Length, Point2D, Vector2D, Rect, TypedSize2D, TypedScale}; use gfx_traits::Epoch; use hyper::header::Headers; use hyper::method::Method; @@ -69,7 +69,7 @@ use style_traits::CSSPixel; use style_traits::SpeculativePainter; use style_traits::cursor::CursorKind; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; -use webrender_api::{ExternalScrollId, DevicePixel, DocumentId, ImageKey}; +use webrender_api::{ExternalScrollId, DevicePixel, DeviceUintSize, DocumentId, ImageKey}; use webvr_traits::{WebVREvent, WebVRMsg}; pub use script_msg::{LayoutMsg, ScriptMsg, EventResult, LogEntry}; @@ -650,7 +650,7 @@ pub enum WebDriverCommandMsg { /// Act as if keys were pressed in the browsing context with the given ID. SendKeys(BrowsingContextId, Vec<(Key, KeyModifiers, KeyState)>), /// Set the window size. - SetWindowSize(TopLevelBrowsingContextId, Size2D, IpcSender), + SetWindowSize(TopLevelBrowsingContextId, DeviceUintSize, IpcSender), /// Take a screenshot of the window. TakeScreenshot(TopLevelBrowsingContextId, IpcSender>), } diff --git a/servo/components/script_traits/script_msg.rs b/servo/components/script_traits/script_msg.rs index 524a87d039af..153adbd98fe8 100644 --- a/servo/components/script_traits/script_msg.rs +++ b/servo/components/script_traits/script_msg.rs @@ -13,7 +13,7 @@ use WorkerGlobalScopeInit; use WorkerScriptLoadOrigin; use canvas_traits::canvas::CanvasMsg; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; -use euclid::{Point2D, Size2D, TypedSize2D}; +use euclid::{Size2D, TypedSize2D}; use gfx_traits::Epoch; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use msg::constellation_msg::{BrowsingContextId, PipelineId, TraversalDirection}; @@ -26,6 +26,7 @@ use servo_url::ServoUrl; use style_traits::CSSPixel; use style_traits::cursor::CursorKind; use style_traits::viewport::ViewportConstraints; +use webrender_api::{DeviceIntPoint, DeviceUintSize}; /// Messages from the layout to the constellation. #[derive(Deserialize, Serialize)] @@ -136,11 +137,11 @@ pub enum ScriptMsg { /// Send a key event SendKeyEvent(Option, Key, KeyState, KeyModifiers), /// Get Window Informations size and position - GetClientWindow(IpcSender<(Size2D, Point2D)>), + GetClientWindow(IpcSender<(DeviceUintSize, DeviceIntPoint)>), /// Move the window to a point - MoveTo(Point2D), + MoveTo(DeviceIntPoint), /// Resize the window to size - ResizeTo(Size2D), + ResizeTo(DeviceUintSize), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), /// A log entry, with the top-level browsing context id and thread name @@ -155,9 +156,9 @@ pub enum ScriptMsg { /// Enter or exit fullscreen SetFullscreenState(bool), /// Get the screen size (pixel) - GetScreenSize(IpcSender<(Size2D)>), + GetScreenSize(IpcSender<(DeviceUintSize)>), /// Get the available screen size (pixel) - GetScreenAvailSize(IpcSender<(Size2D)>), + GetScreenAvailSize(IpcSender<(DeviceUintSize)>), /// Requests that the compositor shut down. Exit, } diff --git a/servo/components/servo/lib.rs b/servo/components/servo/lib.rs index f5ebed9f04d9..bc1a7f6a8f15 100644 --- a/servo/components/servo/lib.rs +++ b/servo/components/servo/lib.rs @@ -81,6 +81,7 @@ use constellation::{FromCompositorLogger, FromScriptLogger}; #[cfg(all(not(target_os = "windows"), not(target_os = "ios")))] use constellation::content_process_sandbox_profile; use env_logger::Logger as EnvLogger; +use euclid::Length; #[cfg(all(not(target_os = "windows"), not(target_os = "ios")))] use gaol::sandbox::{ChildSandbox, ChildSandboxMethods}; use gfx::font_cache_thread::FontCacheThread; @@ -133,7 +134,7 @@ impl Servo where Window: WindowMethods + 'static { let opts = opts::get(); // Make sure the gl context is made current. - window.prepare_for_composite(0, 0); + window.prepare_for_composite(Length::new(0), Length::new(0)); // Get both endpoints of a special channel for communication between // the client window and the compositor. This channel is unique because diff --git a/servo/components/webdriver_server/lib.rs b/servo/components/webdriver_server/lib.rs index 0a77762994e2..f660690a96c6 100644 --- a/servo/components/webdriver_server/lib.rs +++ b/servo/components/webdriver_server/lib.rs @@ -27,7 +27,7 @@ extern crate webdriver; mod keys; -use euclid::Size2D; +use euclid::TypedSize2D; use hyper::method::Method::{self, Post}; use image::{DynamicImage, ImageFormat, RgbImage}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -418,7 +418,7 @@ impl Handler { Nullable::Value(v) => v, Nullable::Null => 0, }; - let size = Size2D::new(width as u32, height as u32); + let size = TypedSize2D::new(width as u32, height as u32); let top_level_browsing_context_id = self.session()?.top_level_browsing_context_id; let cmd_msg = WebDriverCommandMsg::SetWindowSize(top_level_browsing_context_id, size, sender.clone()); diff --git a/servo/etc/ci/update-wpt-checkout b/servo/etc/ci/update-wpt-checkout index c6495e51c40a..8da094bf5ab1 100755 --- a/servo/etc/ci/update-wpt-checkout +++ b/servo/etc/ci/update-wpt-checkout @@ -41,7 +41,7 @@ function unsafe_pull_from_upstream() { fi # Update the manifest to include the new changes. - ./mach update-manifest --release || return 3 + ./mach update-manifest || return 3 # Amend the existing commit with the new changes from updating the manifest. git commit -a --amend --no-edit || return 4 diff --git a/servo/ports/servo/glutin_app/window.rs b/servo/ports/servo/glutin_app/window.rs index 542715a85edb..2add73f9b18b 100644 --- a/servo/ports/servo/glutin_app/window.rs +++ b/servo/ports/servo/glutin_app/window.rs @@ -7,7 +7,7 @@ use compositing::compositor_thread::EventLoopWaker; use compositing::windowing::{AnimationState, MouseWindowEvent, WindowEvent}; use compositing::windowing::{WebRenderDebugOption, WindowMethods}; -use euclid::{Point2D, Size2D, TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D}; +use euclid::{Length, TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D}; #[cfg(target_os = "windows")] use gdi32; use gleam::gl; @@ -40,7 +40,7 @@ use style_traits::cursor::CursorKind; use tinyfiledialogs; #[cfg(target_os = "windows")] use user32; -use webrender_api::{DeviceUintRect, DeviceUintSize, ScrollLocation}; +use webrender_api::{DeviceIntPoint, DeviceUintRect, DeviceUintSize, ScrollLocation}; #[cfg(target_os = "windows")] use winapi; use winit; @@ -177,18 +177,18 @@ enum WindowKind { /// The type of a window. pub struct Window { kind: WindowKind, - screen_size: Size2D, + screen_size: TypedSize2D, inner_size: Cell>, mouse_down_button: Cell>, - mouse_down_point: Cell>, + mouse_down_point: Cell>, event_queue: RefCell>, /// id of the top level browsing context. It is unique as tabs /// are not supported yet. None until created. browser_id: Cell>, - mouse_pos: Cell>, + mouse_pos: Cell>, key_modifiers: Cell, current_url: RefCell>, @@ -222,9 +222,7 @@ impl Window { pub fn new(is_foreground: bool, window_size: TypedSize2D) -> Rc { - let win_size: TypedSize2D = - (window_size.to_f32() * window_creation_scale_factor()) - .to_usize().cast().expect("Window size should fit in u32"); + let win_size: DeviceUintSize = (window_size.to_f32() * window_creation_scale_factor()).to_u32(); let width = win_size.to_untyped().width; let height = win_size.to_untyped().height; @@ -237,7 +235,7 @@ impl Window { let screen_size; let inner_size; let window_kind = if opts::get().headless { - screen_size = Size2D::new(width, height); + screen_size = TypedSize2D::new(width, height); inner_size = TypedSize2D::new(width, height); WindowKind::Headless(HeadlessContext::new(width, height)) } else { @@ -268,7 +266,7 @@ impl Window { } let (screen_width, screen_height) = events_loop.get_primary_monitor().get_dimensions(); - screen_size = Size2D::new(screen_width, screen_height); + screen_size = TypedSize2D::new(screen_width, screen_height); // TODO(ajeffrey): can this fail? let (width, height) = glutin_window.get_inner_size().expect("Failed to get window inner size."); inner_size = TypedSize2D::new(width, height); @@ -316,11 +314,11 @@ impl Window { kind: window_kind, event_queue: RefCell::new(vec!()), mouse_down_button: Cell::new(None), - mouse_down_point: Cell::new(Point2D::new(0, 0)), + mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)), browser_id: Cell::new(None), - mouse_pos: Cell::new(Point2D::new(0, 0)), + mouse_pos: Cell::new(TypedPoint2D::new(0, 0)), key_modifiers: Cell::new(GlutinKeyModifiers::empty()), current_url: RefCell::new(None), @@ -422,8 +420,7 @@ impl Window { }, .. } => { if button == MouseButton::Left || button == MouseButton::Right { - let mouse_pos = self.mouse_pos.get(); - self.handle_mouse(button, state, mouse_pos.x, mouse_pos.y); + self.handle_mouse(button, state, self.mouse_pos.get()); } }, Event::WindowEvent { @@ -433,7 +430,7 @@ impl Window { }, .. } => { - self.mouse_pos.set(Point2D::new(x as i32, y as i32)); + self.mouse_pos.set(TypedPoint2D::new(x as i32, y as i32)); self.event_queue.borrow_mut().push( WindowEvent::MouseWindowMoveEventClass(TypedPoint2D::new(x as f32, y as f32))); } @@ -481,7 +478,7 @@ impl Window { } // window.set_inner_size() takes DeviceIndependentPixel. let new_size = TypedSize2D::new(width as f32, height as f32); - let new_size = (new_size / self.hidpi_factor()).cast().expect("Window size should fit in u32"); + let new_size = (new_size / self.hidpi_factor()).to_u32(); if self.inner_size.get() != new_size { self.inner_size.set(new_size); self.event_queue.borrow_mut().push(WindowEvent::Resize); @@ -518,37 +515,37 @@ impl Window { } } - let mouse_pos = self.mouse_pos.get(); - let event = WindowEvent::Scroll(scroll_location, - TypedPoint2D::new(mouse_pos.x as i32, mouse_pos.y as i32), - phase); + let pos = self.mouse_pos.get().to_f32() * self.hidpi_factor(); + let event = WindowEvent::Scroll(scroll_location, pos.to_i32(), phase); self.event_queue.borrow_mut().push(event); } /// Helper function to handle a click - fn handle_mouse(&self, button: winit::MouseButton, action: winit::ElementState, x: i32, y: i32) { + fn handle_mouse(&self, button: winit::MouseButton, + action: winit::ElementState, + coords: TypedPoint2D) { use script_traits::MouseButton; // FIXME(tkuehn): max pixel dist should be based on pixel density let max_pixel_dist = 10f64; + let scaled_coords = coords.to_f32() * self.hidpi_factor(); let event = match action { ElementState::Pressed => { - self.mouse_down_point.set(Point2D::new(x, y)); + self.mouse_down_point.set(coords); self.mouse_down_button.set(Some(button)); - MouseWindowEvent::MouseDown(MouseButton::Left, TypedPoint2D::new(x as f32, y as f32)) + MouseWindowEvent::MouseDown(MouseButton::Left, scaled_coords) } ElementState::Released => { - let mouse_up_event = MouseWindowEvent::MouseUp(MouseButton::Left, - TypedPoint2D::new(x as f32, y as f32)); + let mouse_up_event = MouseWindowEvent::MouseUp(MouseButton::Left, scaled_coords); match self.mouse_down_button.get() { None => mouse_up_event, Some(but) if button == but => { - let pixel_dist = self.mouse_down_point.get() - Point2D::new(x, y); + let pixel_dist = self.mouse_down_point.get() - coords; let pixel_dist = ((pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y) as f64).sqrt(); if pixel_dist < max_pixel_dist { self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(mouse_up_event)); - MouseWindowEvent::Click(MouseButton::Left, TypedPoint2D::new(x as f32, y as f32)) + MouseWindowEvent::Click(MouseButton::Left, scaled_coords) } else { mouse_up_event } @@ -871,7 +868,7 @@ impl WindowMethods for Window { } fn framebuffer_size(&self) -> DeviceUintSize { - (self.inner_size.get().to_f32() * self.hidpi_factor()).to_usize().cast().expect("Window size should fit in u32") + (self.inner_size.get().to_f32() * self.hidpi_factor()).to_u32() } fn window_rect(&self) -> DeviceUintRect { @@ -880,34 +877,32 @@ impl WindowMethods for Window { DeviceUintRect::new(origin, size) } - fn size(&self) -> TypedSize2D { - self.inner_size.get().to_f32() - } - - fn client_window(&self, _: BrowserId) -> (Size2D, Point2D) { - match self.kind { + fn client_window(&self, _: BrowserId) -> (DeviceUintSize, DeviceIntPoint) { + let (size, point) = match self.kind { WindowKind::Window(ref window, ..) => { // TODO(ajeffrey): can this fail? let (width, height) = window.get_outer_size().expect("Failed to get window outer size."); - let size = Size2D::new(width, height); + let size = TypedSize2D::new(width as f32, height as f32); // TODO(ajeffrey): can this fail? let (x, y) = window.get_position().expect("Failed to get window position."); - let origin = Point2D::new(x as i32, y as i32); + let origin = TypedPoint2D::new(x as f32, y as f32); (size, origin) } WindowKind::Headless(ref context) => { - let size = TypedSize2D::new(context.width, context.height); - (size, Point2D::zero()) + let size = TypedSize2D::new(context.width as f32, context.height as f32); + let origin = TypedPoint2D::zero(); + (size, origin) } - } - + }; + let dpr = self.hidpi_factor(); + ((size * dpr).to_u32(), (point * dpr).to_i32()) } - fn screen_size(&self, _: BrowserId) -> Size2D { - self.screen_size + fn screen_size(&self, _: BrowserId) -> DeviceUintSize { + (self.screen_size.to_f32() * self.hidpi_factor()).to_u32() } - fn screen_avail_size(&self, browser_id: BrowserId) -> Size2D { + fn screen_avail_size(&self, browser_id: BrowserId) -> DeviceUintSize { // FIXME: Glutin doesn't have API for available size. Fallback to screen size self.screen_size(browser_id) } @@ -916,19 +911,21 @@ impl WindowMethods for Window { self.animation_state.set(state); } - fn set_inner_size(&self, _: BrowserId, size: Size2D) { + fn set_inner_size(&self, _: BrowserId, size: DeviceUintSize) { match self.kind { WindowKind::Window(ref window, ..) => { + let size = size.to_f32() / self.hidpi_factor(); window.set_inner_size(size.width as u32, size.height as u32) } WindowKind::Headless(..) => {} } } - fn set_position(&self, _: BrowserId, point: Point2D) { + fn set_position(&self, _: BrowserId, point: DeviceIntPoint) { match self.kind { WindowKind::Window(ref window, ..) => { - window.set_position(point.x, point.y) + let point = point.to_f32() / self.hidpi_factor(); + window.set_position(point.x as i32, point.y as i32) } WindowKind::Headless(..) => {} } @@ -1112,7 +1109,7 @@ impl WindowMethods for Window { fn set_favicon(&self, _: BrowserId, _: ServoUrl) { } - fn prepare_for_composite(&self, _width: usize, _height: usize) -> bool { + fn prepare_for_composite(&self, _width: Length, _height: Length) -> bool { true } diff --git a/toolkit/mozapps/extensions/extensions.manifest b/toolkit/mozapps/extensions/extensions.manifest index 9c311c4b97b5..6afbc0b69158 100644 --- a/toolkit/mozapps/extensions/extensions.manifest +++ b/toolkit/mozapps/extensions/extensions.manifest @@ -1,6 +1,8 @@ component {66354bc9-7ed1-4692-ae1d-8da97d6b205e} nsBlocklistService.js process=main contract @mozilla.org/extensions/blocklist;1 {66354bc9-7ed1-4692-ae1d-8da97d6b205e} process=main -category profile-after-change nsBlocklistService @mozilla.org/extensions/blocklist;1 process=main +#ifndef MOZ_BUILD_APP_IS_BROWSER +category profile-after-change nsBlocklistService @mozilla.org/extensions/blocklist;1 process=main +#endif category update-timer nsBlocklistService @mozilla.org/extensions/blocklist;1,getService,blocklist-background-update-timer,extensions.blocklist.interval,86400 component {4399533d-08d1-458c-a87a-235f74451cfa} addonManager.js diff --git a/toolkit/mozapps/extensions/moz.build b/toolkit/mozapps/extensions/moz.build index 2636cffb25a6..3265b3f6e0e2 100644 --- a/toolkit/mozapps/extensions/moz.build +++ b/toolkit/mozapps/extensions/moz.build @@ -9,6 +9,9 @@ SPHINX_TREES['addon-manager'] = 'docs' with Files('docs/**'): SCHEDULES.exclusive = ['docs'] +if CONFIG['MOZ_BUILD_APP'] == 'browser': + DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True + if CONFIG['MOZ_BUILD_APP'] == 'mobile/android': DEFINES['MOZ_FENNEC'] = True diff --git a/toolkit/mozapps/extensions/nsBlocklistService.js b/toolkit/mozapps/extensions/nsBlocklistService.js index 36d81544aaa6..038db2c2ac84 100644 --- a/toolkit/mozapps/extensions/nsBlocklistService.js +++ b/toolkit/mozapps/extensions/nsBlocklistService.js @@ -221,7 +221,6 @@ function parseRegExp(aStr) { function Blocklist() { Services.obs.addObserver(this, "xpcom-shutdown"); - Services.obs.addObserver(this, "sessionstore-windows-restored"); gLoggingEnabled = Services.prefs.getBoolPref(PREF_EM_LOGGING_ENABLED, false); gBlocklistEnabled = Services.prefs.getBoolPref(PREF_BLOCKLIST_ENABLED, true); gBlocklistLevel = Math.min(Services.prefs.getIntPref(PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL), @@ -229,8 +228,6 @@ function Blocklist() { Services.prefs.addObserver("extensions.blocklist.", this); Services.prefs.addObserver(PREF_EM_LOGGING_ENABLED, this); this.wrappedJSObject = this; - // requests from child processes come in here, see receiveMessage. - Services.ppmm.addMessageListener("Blocklist:content-blocklist-updated", this); } Blocklist.prototype = { @@ -255,7 +252,6 @@ Blocklist.prototype = { shutdown() { Services.obs.removeObserver(this, "xpcom-shutdown"); - Services.ppmm.removeMessageListener("Blocklist:content-blocklist-updated", this); Services.prefs.removeObserver("extensions.blocklist.", this); Services.prefs.removeObserver(PREF_EM_LOGGING_ENABLED, this); }, @@ -265,6 +261,12 @@ Blocklist.prototype = { case "xpcom-shutdown": this.shutdown(); break; + case "profile-after-change": + // We're only called here on non-Desktop-Firefox, and use this opportunity to try to + // load the blocklist asynchronously. On desktop Firefox, we load the list from + // nsBrowserGlue after sessionstore-windows-restored. + this.loadBlocklistAsync(); + break; case "nsPref:changed": switch (aData) { case PREF_EM_LOGGING_ENABLED: @@ -282,25 +284,9 @@ Blocklist.prototype = { break; } break; - case "sessionstore-windows-restored": - Services.obs.removeObserver(this, "sessionstore-windows-restored"); - this._preloadBlocklist(); - break; } }, - // Message manager message handlers - receiveMessage(aMsg) { - switch (aMsg.name) { - case "Blocklist:content-blocklist-updated": - Services.obs.notifyObservers(null, "content-blocklist-updated"); - break; - default: - throw new Error("Unknown blocklist message received from content: " + aMsg.name); - } - return undefined; - }, - /* See nsIBlocklistService */ isAddonBlocklisted(addon, appVersion, toolkitVersion) { return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) == @@ -797,13 +783,13 @@ Blocklist.prototype = { this._pluginEntries = null; }, - async _preloadBlocklist() { + async loadBlocklistAsync() { let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST); try { await this._preloadBlocklistFile(profPath); return; } catch (e) { - LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e); + LOG("Blocklist::loadBlocklistAsync: Failed to load XML file " + e); } var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]); @@ -811,10 +797,10 @@ Blocklist.prototype = { await this._preloadBlocklistFile(appFile.path); return; } catch (e) { - LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e); + LOG("Blocklist::loadBlocklistAsync: Failed to load XML file " + e); } - LOG("Blocklist::_preloadBlocklist: no XML File found"); + LOG("Blocklist::loadBlocklistAsync: no XML File found"); }, async _preloadBlocklistFile(path) { diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js b/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js index c5d1111e20a4..deda9d43f098 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js @@ -14,7 +14,7 @@ add_task(async function() { scope.OS.File.read = () => triedToRead = true; blocklist._loadBlocklist(); Assert.ok(blocklist.isLoaded); - await blocklist._preloadBlocklist(); + await blocklist.loadBlocklistAsync(); Assert.ok(!triedToRead); scope.OS.File.read = read; blocklist._clear(); @@ -22,7 +22,7 @@ add_task(async function() { info("sync -> async complete"); // async first. Check that once we preload the content, that is sufficient. - await blocklist._preloadBlocklist(); + await blocklist.loadBlocklistAsync(); Assert.ok(blocklist.isLoaded); // Calling _loadBlocklist now would just re-load the list sync. @@ -40,7 +40,7 @@ add_task(async function() { }); }; - await blocklist._preloadBlocklist(); + await blocklist.loadBlocklistAsync(); // We're mostly just checking this doesn't error out. Assert.ok(blocklist.isLoaded); info("mixed async/sync test complete"); diff --git a/tools/lint/python/flake8.py b/tools/lint/python/flake8.py index 3385e1639e2e..96b788cf1070 100644 --- a/tools/lint/python/flake8.py +++ b/tools/lint/python/flake8.py @@ -131,6 +131,7 @@ def run_process(config, cmd): proc.wait() except KeyboardInterrupt: proc.kill() + return 1 def setup(root): @@ -163,6 +164,7 @@ def lint(paths, config, **lintargs): cmd.extend(['--append-config={}'.format(c) for c in configs]) cmd.extend(paths) - run_process(config, cmd) + if run_process(config, cmd): + break return results diff --git a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp index d607784eaff1..50c70a035cb9 100644 --- a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp +++ b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp @@ -21,7 +21,6 @@ #include "nsIDOMElement.h" #include "nsIDOMEvent.h" #include "nsIDOMEventTarget.h" -#include "nsIDOMFileList.h" #include "nsIDOMFocusEvent.h" #include "nsIDOMGeoPositionError.h" #include "nsIDOMHTMLInputElement.h" @@ -67,7 +66,6 @@ #include "mozilla/dom/ElementBinding.h" #include "mozilla/dom/EventBinding.h" #include "mozilla/dom/EventTargetBinding.h" -#include "mozilla/dom/FileListBinding.h" #include "mozilla/dom/FocusEventBinding.h" #include "mozilla/dom/FrameLoaderBinding.h" #include "mozilla/dom/HTMLAnchorElementBinding.h" @@ -176,7 +174,6 @@ const ComponentsInterfaceShimEntry kComponentsInterfaceShimMap[] = DEFINE_SHIM(Element), DEFINE_SHIM(Event), DEFINE_SHIM(EventTarget), - DEFINE_SHIM(FileList), DEFINE_SHIM(FocusEvent), DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIFrameLoader, FrameLoader), DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMGeoPositionError, PositionError), diff --git a/xpcom/system/nsIBlocklistService.idl b/xpcom/system/nsIBlocklistService.idl index 5591003c5855..0f792e96ae3b 100644 --- a/xpcom/system/nsIBlocklistService.idl +++ b/xpcom/system/nsIBlocklistService.idl @@ -133,6 +133,11 @@ interface nsIBlocklistService : nsISupports * Whether or not we've finished loading the blocklist. */ readonly attribute boolean isLoaded; + + /** + * Trigger loading the blocklist content asynchronously. + */ + void loadBlocklistAsync(); }; /**