diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 2e98004304e7..0f28f0698fb7 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -848,8 +848,10 @@ Accessible::HandleAccEvent(AccEvent* aEvent) if (profiler_is_active()) { nsAutoCString strEventType; GetAccService()->GetStringEventType(aEvent->GetEventType(), strEventType); - - profiler_tracing("A11y Event", strEventType.get()); + nsAutoCString strMarker; + strMarker.AppendLiteral("A11y Event - "); + strMarker.Append(strEventType); + profiler_add_marker(strMarker.get()); } if (IPCAccessibilityActive() && Document()) { diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index df287198e368..afce4b854577 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -327,6 +327,7 @@ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"] } .webextension-browser-action > .toolbarbutton-badge-stack > .toolbarbutton-icon { + height: 16px; width: 16px; } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 9d8acc1b24a1..7d56d2f9e2ad 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1375,6 +1375,25 @@ var gBrowserInit = { gRemoteControl.updateVisualCue(Marionette.running); + // If we are given a tab to swap in, take care of it before first paint to + // avoid an about:blank flash. + let tabToOpen = window.arguments && window.arguments[0]; + if (tabToOpen instanceof XULElement) { + // Clear the reference to the tab from the arguments array. + window.arguments[0] = null; + + // Stop the about:blank load + gBrowser.stop(); + // make sure it has a docshell + gBrowser.docShell; + + try { + gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen); + } catch (e) { + Cu.reportError(e); + } + } + // Wait until chrome is painted before executing code not critical to making the window visible this._boundDelayedStartup = this._delayedStartup.bind(this); window.addEventListener("MozAfterPaint", this._boundDelayedStartup); @@ -1601,6 +1620,8 @@ var gBrowserInit = { return; } + // We don't check if uriToLoad is a XULElement because this case has + // already been handled before first paint, and the argument cleared. if (uriToLoad instanceof Ci.nsIArray) { let count = uriToLoad.length; let specs = []; @@ -1618,39 +1639,6 @@ var gBrowserInit = { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }); } catch (e) {} - } else if (uriToLoad instanceof XULElement) { - // swap the given tab with the default about:blank tab and then close - // the original tab in the other window. - let tabToOpen = uriToLoad; - - // If this tab was passed as a window argument, clear the - // reference to it from the arguments array. - if (window.arguments[0] == tabToOpen) { - window.arguments[0] = null; - } - - // Stop the about:blank load - gBrowser.stop(); - // make sure it has a docshell - gBrowser.docShell; - - // We must set usercontextid before updateBrowserRemoteness() - // so that the newly created remote tab child has correct usercontextid - if (tabToOpen.hasAttribute("usercontextid")) { - let usercontextid = tabToOpen.getAttribute("usercontextid"); - gBrowser.selectedBrowser.setAttribute("usercontextid", usercontextid); - } - - try { - // Make sure selectedBrowser has the same remote settings as the one - // we are swapping in. - gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, - tabToOpen.linkedBrowser.isRemoteBrowser, - { remoteType: tabToOpen.linkedBrowser.remoteType }); - gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen); - } catch (e) { - Cu.reportError(e); - } } else if (window.arguments.length >= 3) { // window.arguments[2]: referrer (nsIURI | string) // [3]: postData (nsIInputStream) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 55c0945cc684..605747189330 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3009,6 +3009,16 @@ newTab = true; } + aTab._endRemoveArgs = [closeWindow, newTab]; + + // swapBrowsersAndCloseOther will take care of closing the window without animation. + if (closeWindow && aAdoptedByTab) { + // Remove the tab's filter to avoid leaking. + if (aTab.linkedPanel) { + this._tabFilters.delete(aTab); + } + return true; + } if (!aTab._fullyOpen) { // If the opening tab animation hasn't finished before we start closing the @@ -3079,7 +3089,6 @@ tab.owner = null; } - aTab._endRemoveArgs = [closeWindow, newTab]; return true; ]]> @@ -3297,6 +3306,25 @@ if (!remoteBrowser._beginRemoveTab(aOtherTab, aOurTab, true)) return; + // If this is the last tab of the window, hide the window + // immediately without animation before the docshell swap, to avoid + // about:blank being painted. + let [closeWindow] = aOtherTab._endRemoveArgs; + if (closeWindow) { + let win = aOtherTab.ownerGlobal; + let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + dwu.suppressAnimation(true); + // Only suppressing window animations isn't enough to avoid + // an empty content area being painted. + let baseWin = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell) + .QueryInterface(Ci.nsIDocShellTreeItem) + .treeOwner + .QueryInterface(Ci.nsIBaseWindow); + baseWin.visibility = false; + } + let modifiedAttrs = []; if (aOtherTab.hasAttribute("muted")) { aOurTab.setAttribute("muted", "true"); @@ -3363,7 +3391,11 @@ } // Finish tearing down the tab that's going away. - remoteBrowser._endRemoveTab(aOtherTab); + if (closeWindow) { + aOtherTab.ownerGlobal.close(); + } else { + remoteBrowser._endRemoveTab(aOtherTab); + } this.setTabTitle(aOurTab); @@ -3729,6 +3761,14 @@ for (var name in aOptions) options += "," + name + "=" + aOptions[name]; + // Play the tab closing animation to give immediate feedback while + // waiting for the new window to appear. + // content area when the docshells are swapped. + if (this.animationsEnabled) { + aTab.style.maxWidth = ""; // ensure that fade-out transition happens + aTab.removeAttribute("fadein"); + } + // tell a new window to take the "dropped" tab return window.openDialog(getBrowserURL(), "_blank", options, aTab); ]]> @@ -3851,7 +3891,8 @@ let linkedBrowser = aTab.linkedBrowser; let params = { eventDetail: { adoptedTab: aTab }, preferredRemoteType: linkedBrowser.remoteType, - sameProcessAsFrameLoader: linkedBrowser.frameLoader }; + sameProcessAsFrameLoader: linkedBrowser.frameLoader, + skipAnimation: true }; if (aTab.hasAttribute("usercontextid")) { // new tab must have the same usercontextid as the old one params.userContextId = aTab.getAttribute("usercontextid"); @@ -7399,7 +7440,7 @@ window.moveTo(left, top); window.focus(); } else { - let props = { screenX: left, screenY: top }; + let props = { screenX: left, screenY: top, suppressanimation: 1 }; if (AppConstants.platform != "win") { props.outerWidth = winWidth; props.outerHeight = winHeight; diff --git a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js index b4ef2c6ae9d9..48138e1833a6 100644 --- a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js +++ b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js @@ -67,7 +67,7 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [ "_invalidate@chrome://global/content/bindings/autocomplete.xml", "invalidate@chrome://global/content/bindings/autocomplete.xml" ], - times: 1584, // This number should only ever go down - never up. + times: 1344, // This number should only ever go down - never up. }, { diff --git a/browser/base/content/test/performance/browser_urlbar_search_reflows.js b/browser/base/content/test/performance/browser_urlbar_search_reflows.js index 636379ccc4df..1b3f884d3bd4 100644 --- a/browser/base/content/test/performance/browser_urlbar_search_reflows.js +++ b/browser/base/content/test/performance/browser_urlbar_search_reflows.js @@ -67,7 +67,7 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [ "_invalidate@chrome://global/content/bindings/autocomplete.xml", "invalidate@chrome://global/content/bindings/autocomplete.xml" ], - times: 390, // This number should only ever go down - never up. + times: 330, // This number should only ever go down - never up. }, { @@ -119,7 +119,7 @@ const EXPECTED_REFLOWS_SECOND_OPEN = [ "_invalidate@chrome://global/content/bindings/autocomplete.xml", "invalidate@chrome://global/content/bindings/autocomplete.xml" ], - times: 444, // This number should only ever go down - never up. + times: 384, // This number should only ever go down - never up. }, // Bug 1384256 diff --git a/browser/components/customizableui/content/panelUI.js b/browser/components/customizableui/content/panelUI.js index ed22bf402ab8..20a7255f975f 100644 --- a/browser/components/customizableui/content/panelUI.js +++ b/browser/components/customizableui/content/panelUI.js @@ -318,15 +318,15 @@ const PanelUI = { * * @return a Promise that resolves once the panel is ready to roll. */ - ensureReady() { - if (this._readyPromise) { - return this._readyPromise; + async ensureReady() { + if (this._isReady) { + return; } + + await window.delayedStartupPromise; this._ensureEventListenersAdded(); this.panel.hidden = false; - this._readyPromise = Promise.resolve(); this._isReady = true; - return this._readyPromise; }, /** diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini index eb8f34e79f1d..8a36882ce716 100644 --- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -41,6 +41,7 @@ skip-if = os == 'linux' [browser_ext_browserAction_pageAction_icon.js] [browser_ext_browserAction_pageAction_icon_permissions.js] [browser_ext_browserAction_popup.js] +skip-if = debug && (os == 'linux' && bits == 32) # Bug 1313372 [browser_ext_browserAction_popup_preload.js] skip-if = (os == 'win' && !debug) # bug 1352668 [browser_ext_browserAction_popup_resize.js] diff --git a/browser/extensions/formautofill/FormAutofillContent.jsm b/browser/extensions/formautofill/FormAutofillContent.jsm index 87f1e3861be7..8ffec2550110 100644 --- a/browser/extensions/formautofill/FormAutofillContent.jsm +++ b/browser/extensions/formautofill/FormAutofillContent.jsm @@ -372,12 +372,10 @@ var FormAutofillContent = { * * @param {Object} profile Submitted form's address/creditcard guid and record. * @param {Object} domWin Current content window. - * @param {int} timeStartedFillingMS Time of form filling started. */ - _onFormSubmit(profile, domWin, timeStartedFillingMS) { + _onFormSubmit(profile, domWin) { let mm = this._messageManagerFromWindow(domWin); - mm.sendAsyncMessage("FormAutofill:OnFormSubmit", - {profile, timeStartedFillingMS}); + mm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile); }, /** @@ -409,7 +407,7 @@ var FormAutofillContent = { return true; } - this._onFormSubmit(records, domWin, handler.timeStartedFillingMS); + this._onFormSubmit(records, domWin); return true; }, diff --git a/browser/extensions/formautofill/FormAutofillHandler.jsm b/browser/extensions/formautofill/FormAutofillHandler.jsm index e1233c989a8d..935fbb941d32 100644 --- a/browser/extensions/formautofill/FormAutofillHandler.jsm +++ b/browser/extensions/formautofill/FormAutofillHandler.jsm @@ -114,11 +114,6 @@ FormAutofillHandler.prototype = { return this._formFieldCount != this.form.elements.length; }, - /** - * Time in milliseconds since epoch when a user started filling in the form. - */ - timeStartedFillingMS: null, - /** * Set fieldDetails from the form about fields that can be autofilled. * @@ -132,7 +127,6 @@ FormAutofillHandler.prototype = { this._formFieldCount = this.form.elements.length; let fieldDetails = FormAutofillHeuristics.getFormInfo(this.form, allowDuplicates); this.fieldDetails = fieldDetails ? fieldDetails : []; - this.form.rootElement.addEventListener("input", this); log.debug("Collected details on", this.fieldDetails.length, "fields"); this.address.fieldDetails = this.fieldDetails.filter( @@ -588,43 +582,4 @@ FormAutofillHandler.prototype = { Services.cpmm.sendAsyncMessage("FormAutofill:GetDecryptedString", {cipherText, reauth}); }); }, - - /** - * Find the fieldDetail by HTML element (assume all details were collected in collectFormFields). - * - * @param {HTMLElement} element - * - * @returns {Object|null} - * Return fieldDetail if fieldDetail's element ref could match the target. - * (or return null if the element could not match any fieldDetail). - */ - getFieldDetailsForElement(element) { - for (let detail of this.fieldDetails) { - if (detail.elementWeakRef.get() == element) { - return detail; - } - } - return null; - }, - - handleEvent(event) { - switch (event.type) { - case "input": - if (!event.isTrusted) { - return; - } - - if (!FormAutofillUtils.isFieldEligibleForAutofill(event.target)) { - return; - } - - if (!this.getFieldDetailsForElement(event.target)) { - return; - } - - this.form.rootElement.removeEventListener("input", this); - this.timeStartedFillingMS = Date.now(); - break; - } - }, }; diff --git a/browser/extensions/formautofill/FormAutofillParent.jsm b/browser/extensions/formautofill/FormAutofillParent.jsm index 1585517d562a..a360828669a5 100644 --- a/browser/extensions/formautofill/FormAutofillParent.jsm +++ b/browser/extensions/formautofill/FormAutofillParent.jsm @@ -349,7 +349,7 @@ FormAutofillParent.prototype = { this._updateStatus(); }, - _onAddressSubmit(address, target, timeStartedFillingMS) { + _onAddressSubmit(address, target) { if (address.guid) { // Avoid updating the fields that users don't modify. let originalAddress = this.profileStorage.addresses.get(address.guid); @@ -360,8 +360,6 @@ FormAutofillParent.prototype = { } if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) { - this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS); - FormAutofillDoorhanger.show(target, "update").then((state) => { let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record); switch (state) { @@ -385,7 +383,6 @@ FormAutofillParent.prototype = { Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill_update", 1); return; } - this._recordFormFillingTime("address", "autofill", timeStartedFillingMS); this.profileStorage.addresses.notifyUsed(address.guid); // Address is merged successfully Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1); @@ -395,7 +392,6 @@ FormAutofillParent.prototype = { changedGUIDs.push(this.profileStorage.addresses.add(address.record)); } changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid)); - this._recordFormFillingTime("address", "manual", timeStartedFillingMS); // Show first time use doorhanger if (Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) { @@ -439,28 +435,13 @@ FormAutofillParent.prototype = { }, _onFormSubmit(data, target) { - let {profile: {address, creditCard}, timeStartedFillingMS} = data; + let {address, creditCard} = data; if (address) { - this._onAddressSubmit(address, target, timeStartedFillingMS); + this._onAddressSubmit(address, target); } if (creditCard) { this._onCreditCardSubmit(creditCard, target); } }, - /** - * Set the probes for the filling time with specific filling type and form type. - * - * @private - * @param {string} formType - * 3 type of form (address/creditcard/address-creditcard). - * @param {string} fillingType - * 3 filling type (manual/autofill/autofill-update). - * @param {int} startedFillingMS - * Time that form started to filling in ms. - */ - _recordFormFillingTime(formType, fillingType, startedFillingMS) { - let histogram = Services.telemetry.getKeyedHistogramById("FORM_FILLING_REQUIRED_TIME_MS"); - histogram.add(`${formType}-${fillingType}`, Date.now() - startedFillingMS); - }, }; diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 48965f87b2c8..677ae103042d 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,5 +1,5 @@ This is the PDF.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.9.489 +Current extension version is: 1.9.512 -Taken from upstream commit: b7fcaff0 +Taken from upstream commit: 066fea9c diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 6de8d9dfd6c9..290e5f9e9982 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -1023,6 +1023,12 @@ function wrapReason(reason) { return new UnknownErrorException(reason.message, reason.details); } } +function makeReasonSerializable(reason) { + if (!(reason instanceof Error) || reason instanceof AbortException || reason instanceof MissingPDFException || reason instanceof UnexpectedResponseException || reason instanceof UnknownErrorException) { + return reason; + } + return new UnknownErrorException(reason.message, reason.toString()); +} function resolveOrReject(capability, success, reason) { if (success) { capability.resolve(); @@ -1080,15 +1086,12 @@ function MessageHandler(sourceName, targetName, comObj) { data: result }); }, reason => { - if (reason instanceof Error) { - reason = reason + ''; - } comObj.postMessage({ sourceName, targetName, isReply: true, callbackId: data.callbackId, - error: reason + error: makeReasonSerializable(reason) }); }); } else if (data.streamId) { @@ -1739,17 +1742,16 @@ var _dom_utils = __w_pdfjs_require__(1); var _util = __w_pdfjs_require__(0); -function AnnotationElementFactory() {} -AnnotationElementFactory.prototype = { - create: function AnnotationElementFactory_create(parameters) { - var subtype = parameters.data.annotationType; +class AnnotationElementFactory { + static create(parameters) { + let subtype = parameters.data.annotationType; switch (subtype) { case _util.AnnotationType.LINK: return new LinkAnnotationElement(parameters); case _util.AnnotationType.TEXT: return new TextAnnotationElement(parameters); case _util.AnnotationType.WIDGET: - var fieldType = parameters.data.fieldType; + let fieldType = parameters.data.fieldType; switch (fieldType) { case 'Tx': return new TextWidgetAnnotationElement(parameters); @@ -1783,10 +1785,10 @@ AnnotationElementFactory.prototype = { return new AnnotationElement(parameters); } } -}; -var AnnotationElement = function AnnotationElementClosure() { - function AnnotationElement(parameters, isRenderable, ignoreBorder) { - this.isRenderable = isRenderable || false; +} +class AnnotationElement { + constructor(parameters, isRenderable = false, ignoreBorder = false) { + this.isRenderable = isRenderable; this.data = parameters.data; this.layer = parameters.layer; this.page = parameters.page; @@ -1799,335 +1801,304 @@ var AnnotationElement = function AnnotationElementClosure() { this.container = this._createContainer(ignoreBorder); } } - AnnotationElement.prototype = { - _createContainer: function AnnotationElement_createContainer(ignoreBorder) { - var data = this.data, - page = this.page, - viewport = this.viewport; - var container = document.createElement('section'); - var width = data.rect[2] - data.rect[0]; - var height = data.rect[3] - data.rect[1]; - container.setAttribute('data-annotation-id', data.id); - var rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]); - _dom_utils.CustomStyle.setProp('transform', container, 'matrix(' + viewport.transform.join(',') + ')'); - _dom_utils.CustomStyle.setProp('transformOrigin', container, -rect[0] + 'px ' + -rect[1] + 'px'); - if (!ignoreBorder && data.borderStyle.width > 0) { - container.style.borderWidth = data.borderStyle.width + 'px'; - if (data.borderStyle.style !== _util.AnnotationBorderStyleType.UNDERLINE) { - width = width - 2 * data.borderStyle.width; - height = height - 2 * data.borderStyle.width; - } - var horizontalRadius = data.borderStyle.horizontalCornerRadius; - var verticalRadius = data.borderStyle.verticalCornerRadius; - if (horizontalRadius > 0 || verticalRadius > 0) { - var radius = horizontalRadius + 'px / ' + verticalRadius + 'px'; - _dom_utils.CustomStyle.setProp('borderRadius', container, radius); - } - switch (data.borderStyle.style) { - case _util.AnnotationBorderStyleType.SOLID: - container.style.borderStyle = 'solid'; - break; - case _util.AnnotationBorderStyleType.DASHED: - container.style.borderStyle = 'dashed'; - break; - case _util.AnnotationBorderStyleType.BEVELED: - (0, _util.warn)('Unimplemented border style: beveled'); - break; - case _util.AnnotationBorderStyleType.INSET: - (0, _util.warn)('Unimplemented border style: inset'); - break; - case _util.AnnotationBorderStyleType.UNDERLINE: - container.style.borderBottomStyle = 'solid'; - break; - default: - break; - } - if (data.color) { - container.style.borderColor = _util.Util.makeCssRgb(data.color[0] | 0, data.color[1] | 0, data.color[2] | 0); - } else { - container.style.borderWidth = 0; - } + _createContainer(ignoreBorder = false) { + let data = this.data, + page = this.page, + viewport = this.viewport; + let container = document.createElement('section'); + let width = data.rect[2] - data.rect[0]; + let height = data.rect[3] - data.rect[1]; + container.setAttribute('data-annotation-id', data.id); + let rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]); + _dom_utils.CustomStyle.setProp('transform', container, 'matrix(' + viewport.transform.join(',') + ')'); + _dom_utils.CustomStyle.setProp('transformOrigin', container, -rect[0] + 'px ' + -rect[1] + 'px'); + if (!ignoreBorder && data.borderStyle.width > 0) { + container.style.borderWidth = data.borderStyle.width + 'px'; + if (data.borderStyle.style !== _util.AnnotationBorderStyleType.UNDERLINE) { + width = width - 2 * data.borderStyle.width; + height = height - 2 * data.borderStyle.width; } - container.style.left = rect[0] + 'px'; - container.style.top = rect[1] + 'px'; - container.style.width = width + 'px'; - container.style.height = height + 'px'; - return container; - }, - _createPopup: function AnnotationElement_createPopup(container, trigger, data) { - if (!trigger) { - trigger = document.createElement('div'); - trigger.style.height = container.style.height; - trigger.style.width = container.style.width; - container.appendChild(trigger); + let horizontalRadius = data.borderStyle.horizontalCornerRadius; + let verticalRadius = data.borderStyle.verticalCornerRadius; + if (horizontalRadius > 0 || verticalRadius > 0) { + let radius = horizontalRadius + 'px / ' + verticalRadius + 'px'; + _dom_utils.CustomStyle.setProp('borderRadius', container, radius); + } + switch (data.borderStyle.style) { + case _util.AnnotationBorderStyleType.SOLID: + container.style.borderStyle = 'solid'; + break; + case _util.AnnotationBorderStyleType.DASHED: + container.style.borderStyle = 'dashed'; + break; + case _util.AnnotationBorderStyleType.BEVELED: + (0, _util.warn)('Unimplemented border style: beveled'); + break; + case _util.AnnotationBorderStyleType.INSET: + (0, _util.warn)('Unimplemented border style: inset'); + break; + case _util.AnnotationBorderStyleType.UNDERLINE: + container.style.borderBottomStyle = 'solid'; + break; + default: + break; + } + if (data.color) { + container.style.borderColor = _util.Util.makeCssRgb(data.color[0] | 0, data.color[1] | 0, data.color[2] | 0); + } else { + container.style.borderWidth = 0; } - var popupElement = new PopupElement({ - container, - trigger, - color: data.color, - title: data.title, - contents: data.contents, - hideWrapper: true - }); - var popup = popupElement.render(); - popup.style.left = container.style.width; - container.appendChild(popup); - }, - render: function AnnotationElement_render() { - throw new Error('Abstract method AnnotationElement.render called'); } - }; - return AnnotationElement; -}(); -var LinkAnnotationElement = function LinkAnnotationElementClosure() { - function LinkAnnotationElement(parameters) { - AnnotationElement.call(this, parameters, true); + container.style.left = rect[0] + 'px'; + container.style.top = rect[1] + 'px'; + container.style.width = width + 'px'; + container.style.height = height + 'px'; + return container; } - _util.Util.inherit(LinkAnnotationElement, AnnotationElement, { - render: function LinkAnnotationElement_render() { - this.container.className = 'linkAnnotation'; - var link = document.createElement('a'); - (0, _dom_utils.addLinkAttributes)(link, { - url: this.data.url, - target: this.data.newWindow ? _dom_utils.LinkTarget.BLANK : undefined - }); - if (!this.data.url) { - if (this.data.action) { - this._bindNamedAction(link, this.data.action); - } else { - this._bindLink(link, this.data.dest); - } + _createPopup(container, trigger, data) { + if (!trigger) { + trigger = document.createElement('div'); + trigger.style.height = container.style.height; + trigger.style.width = container.style.width; + container.appendChild(trigger); + } + let popupElement = new PopupElement({ + container, + trigger, + color: data.color, + title: data.title, + contents: data.contents, + hideWrapper: true + }); + let popup = popupElement.render(); + popup.style.left = container.style.width; + container.appendChild(popup); + } + render() { + throw new Error('Abstract method `AnnotationElement.render` called'); + } +} +class LinkAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, true); + } + render() { + this.container.className = 'linkAnnotation'; + let link = document.createElement('a'); + (0, _dom_utils.addLinkAttributes)(link, { + url: this.data.url, + target: this.data.newWindow ? _dom_utils.LinkTarget.BLANK : undefined + }); + if (!this.data.url) { + if (this.data.action) { + this._bindNamedAction(link, this.data.action); + } else { + this._bindLink(link, this.data.dest); } - this.container.appendChild(link); - return this.container; - }, - _bindLink(link, destination) { - link.href = this.linkService.getDestinationHash(destination); - link.onclick = () => { - if (destination) { - this.linkService.navigateTo(destination); - } - return false; - }; + } + this.container.appendChild(link); + return this.container; + } + _bindLink(link, destination) { + link.href = this.linkService.getDestinationHash(destination); + link.onclick = () => { if (destination) { - link.className = 'internalLink'; + this.linkService.navigateTo(destination); } - }, - _bindNamedAction(link, action) { - link.href = this.linkService.getAnchorUrl(''); - link.onclick = () => { - this.linkService.executeNamedAction(action); - return false; - }; + return false; + }; + if (destination) { link.className = 'internalLink'; } - }); - return LinkAnnotationElement; -}(); -var TextAnnotationElement = function TextAnnotationElementClosure() { - function TextAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable); } - _util.Util.inherit(TextAnnotationElement, AnnotationElement, { - render: function TextAnnotationElement_render() { - this.container.className = 'textAnnotation'; - var image = document.createElement('img'); - image.style.height = this.container.style.height; - image.style.width = this.container.style.width; - image.src = this.imageResourcesPath + 'annotation-' + this.data.name.toLowerCase() + '.svg'; - image.alt = '[{{type}} Annotation]'; - image.dataset.l10nId = 'text_annotation_type'; - image.dataset.l10nArgs = JSON.stringify({ type: this.data.name }); - if (!this.data.hasPopup) { - this._createPopup(this.container, image, this.data); - } - this.container.appendChild(image); - return this.container; + _bindNamedAction(link, action) { + link.href = this.linkService.getAnchorUrl(''); + link.onclick = () => { + this.linkService.executeNamedAction(action); + return false; + }; + link.className = 'internalLink'; + } +} +class TextAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable); + } + render() { + this.container.className = 'textAnnotation'; + let image = document.createElement('img'); + image.style.height = this.container.style.height; + image.style.width = this.container.style.width; + image.src = this.imageResourcesPath + 'annotation-' + this.data.name.toLowerCase() + '.svg'; + image.alt = '[{{type}} Annotation]'; + image.dataset.l10nId = 'text_annotation_type'; + image.dataset.l10nArgs = JSON.stringify({ type: this.data.name }); + if (!this.data.hasPopup) { + this._createPopup(this.container, image, this.data); } - }); - return TextAnnotationElement; -}(); -var WidgetAnnotationElement = function WidgetAnnotationElementClosure() { - function WidgetAnnotationElement(parameters, isRenderable) { - AnnotationElement.call(this, parameters, isRenderable); + this.container.appendChild(image); + return this.container; } - _util.Util.inherit(WidgetAnnotationElement, AnnotationElement, { - render: function WidgetAnnotationElement_render() { - return this.container; - } - }); - return WidgetAnnotationElement; -}(); -var TextWidgetAnnotationElement = function TextWidgetAnnotationElementClosure() { - var TEXT_ALIGNMENT = ['left', 'center', 'right']; - function TextWidgetAnnotationElement(parameters) { - var isRenderable = parameters.renderInteractiveForms || !parameters.data.hasAppearance && !!parameters.data.fieldValue; - WidgetAnnotationElement.call(this, parameters, isRenderable); +} +class WidgetAnnotationElement extends AnnotationElement { + render() { + return this.container; } - _util.Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, { - render: function TextWidgetAnnotationElement_render() { - this.container.className = 'textWidgetAnnotation'; - var element = null; - if (this.renderInteractiveForms) { - if (this.data.multiLine) { - element = document.createElement('textarea'); - element.textContent = this.data.fieldValue; - } else { - element = document.createElement('input'); - element.type = 'text'; - element.setAttribute('value', this.data.fieldValue); - } - element.disabled = this.data.readOnly; - if (this.data.maxLen !== null) { - element.maxLength = this.data.maxLen; - } - if (this.data.comb) { - var fieldWidth = this.data.rect[2] - this.data.rect[0]; - var combWidth = fieldWidth / this.data.maxLen; - element.classList.add('comb'); - element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)'; - } - } else { - element = document.createElement('div'); +} +class TextWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + let isRenderable = parameters.renderInteractiveForms || !parameters.data.hasAppearance && !!parameters.data.fieldValue; + super(parameters, isRenderable); + } + render() { + const TEXT_ALIGNMENT = ['left', 'center', 'right']; + this.container.className = 'textWidgetAnnotation'; + let element = null; + if (this.renderInteractiveForms) { + if (this.data.multiLine) { + element = document.createElement('textarea'); element.textContent = this.data.fieldValue; - element.style.verticalAlign = 'middle'; - element.style.display = 'table-cell'; - var font = null; - if (this.data.fontRefName) { - font = this.page.commonObjs.getData(this.data.fontRefName); - } - this._setTextStyle(element, font); + } else { + element = document.createElement('input'); + element.type = 'text'; + element.setAttribute('value', this.data.fieldValue); } - if (this.data.textAlignment !== null) { - element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; - } - this.container.appendChild(element); - return this.container; - }, - _setTextStyle: function TextWidgetAnnotationElement_setTextStyle(element, font) { - var style = element.style; - style.fontSize = this.data.fontSize + 'px'; - style.direction = this.data.fontDirection < 0 ? 'rtl' : 'ltr'; - if (!font) { - return; - } - style.fontWeight = font.black ? font.bold ? '900' : 'bold' : font.bold ? 'bold' : 'normal'; - style.fontStyle = font.italic ? 'italic' : 'normal'; - var fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : ''; - var fallbackName = font.fallbackName || 'Helvetica, sans-serif'; - style.fontFamily = fontFamily + fallbackName; - } - }); - return TextWidgetAnnotationElement; -}(); -var CheckboxWidgetAnnotationElement = function CheckboxWidgetAnnotationElementClosure() { - function CheckboxWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); - } - _util.Util.inherit(CheckboxWidgetAnnotationElement, WidgetAnnotationElement, { - render: function CheckboxWidgetAnnotationElement_render() { - this.container.className = 'buttonWidgetAnnotation checkBox'; - var element = document.createElement('input'); element.disabled = this.data.readOnly; - element.type = 'checkbox'; - if (this.data.fieldValue && this.data.fieldValue !== 'Off') { - element.setAttribute('checked', true); + if (this.data.maxLen !== null) { + element.maxLength = this.data.maxLen; } - this.container.appendChild(element); - return this.container; + if (this.data.comb) { + let fieldWidth = this.data.rect[2] - this.data.rect[0]; + let combWidth = fieldWidth / this.data.maxLen; + element.classList.add('comb'); + element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)'; + } + } else { + element = document.createElement('div'); + element.textContent = this.data.fieldValue; + element.style.verticalAlign = 'middle'; + element.style.display = 'table-cell'; + let font = null; + if (this.data.fontRefName) { + font = this.page.commonObjs.getData(this.data.fontRefName); + } + this._setTextStyle(element, font); } - }); - return CheckboxWidgetAnnotationElement; -}(); -var RadioButtonWidgetAnnotationElement = function RadioButtonWidgetAnnotationElementClosure() { - function RadioButtonWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); + if (this.data.textAlignment !== null) { + element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; + } + this.container.appendChild(element); + return this.container; } - _util.Util.inherit(RadioButtonWidgetAnnotationElement, WidgetAnnotationElement, { - render: function RadioButtonWidgetAnnotationElement_render() { - this.container.className = 'buttonWidgetAnnotation radioButton'; - var element = document.createElement('input'); - element.disabled = this.data.readOnly; - element.type = 'radio'; - element.name = this.data.fieldName; - if (this.data.fieldValue === this.data.buttonValue) { - element.setAttribute('checked', true); - } - this.container.appendChild(element); - return this.container; + _setTextStyle(element, font) { + let style = element.style; + style.fontSize = this.data.fontSize + 'px'; + style.direction = this.data.fontDirection < 0 ? 'rtl' : 'ltr'; + if (!font) { + return; } - }); - return RadioButtonWidgetAnnotationElement; -}(); -var ChoiceWidgetAnnotationElement = function ChoiceWidgetAnnotationElementClosure() { - function ChoiceWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); + style.fontWeight = font.black ? font.bold ? '900' : 'bold' : font.bold ? 'bold' : 'normal'; + style.fontStyle = font.italic ? 'italic' : 'normal'; + let fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : ''; + let fallbackName = font.fallbackName || 'Helvetica, sans-serif'; + style.fontFamily = fontFamily + fallbackName; } - _util.Util.inherit(ChoiceWidgetAnnotationElement, WidgetAnnotationElement, { - render: function ChoiceWidgetAnnotationElement_render() { - this.container.className = 'choiceWidgetAnnotation'; - var selectElement = document.createElement('select'); - selectElement.disabled = this.data.readOnly; - if (!this.data.combo) { - selectElement.size = this.data.options.length; - if (this.data.multiSelect) { - selectElement.multiple = true; - } - } - for (var i = 0, ii = this.data.options.length; i < ii; i++) { - var option = this.data.options[i]; - var optionElement = document.createElement('option'); - optionElement.textContent = option.displayValue; - optionElement.value = option.exportValue; - if (this.data.fieldValue.indexOf(option.displayValue) >= 0) { - optionElement.setAttribute('selected', true); - } - selectElement.appendChild(optionElement); - } - this.container.appendChild(selectElement); - return this.container; - } - }); - return ChoiceWidgetAnnotationElement; -}(); -var PopupAnnotationElement = function PopupAnnotationElementClosure() { - var IGNORE_TYPES = ['Line']; - function PopupAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable); +} +class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); } - _util.Util.inherit(PopupAnnotationElement, AnnotationElement, { - render: function PopupAnnotationElement_render() { - this.container.className = 'popupAnnotation'; - if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) { - return this.container; + render() { + this.container.className = 'buttonWidgetAnnotation checkBox'; + let element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'checkbox'; + if (this.data.fieldValue && this.data.fieldValue !== 'Off') { + element.setAttribute('checked', true); + } + this.container.appendChild(element); + return this.container; + } +} +class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); + } + render() { + this.container.className = 'buttonWidgetAnnotation radioButton'; + let element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'radio'; + element.name = this.data.fieldName; + if (this.data.fieldValue === this.data.buttonValue) { + element.setAttribute('checked', true); + } + this.container.appendChild(element); + return this.container; + } +} +class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); + } + render() { + this.container.className = 'choiceWidgetAnnotation'; + let selectElement = document.createElement('select'); + selectElement.disabled = this.data.readOnly; + if (!this.data.combo) { + selectElement.size = this.data.options.length; + if (this.data.multiSelect) { + selectElement.multiple = true; } - var selector = '[data-annotation-id="' + this.data.parentId + '"]'; - var parentElement = this.layer.querySelector(selector); - if (!parentElement) { - return this.container; + } + for (let i = 0, ii = this.data.options.length; i < ii; i++) { + let option = this.data.options[i]; + let optionElement = document.createElement('option'); + optionElement.textContent = option.displayValue; + optionElement.value = option.exportValue; + if (this.data.fieldValue.indexOf(option.displayValue) >= 0) { + optionElement.setAttribute('selected', true); } - var popup = new PopupElement({ - container: this.container, - trigger: parentElement, - color: this.data.color, - title: this.data.title, - contents: this.data.contents - }); - var parentLeft = parseFloat(parentElement.style.left); - var parentWidth = parseFloat(parentElement.style.width); - _dom_utils.CustomStyle.setProp('transformOrigin', this.container, -(parentLeft + parentWidth) + 'px -' + parentElement.style.top); - this.container.style.left = parentLeft + parentWidth + 'px'; - this.container.appendChild(popup.render()); + selectElement.appendChild(optionElement); + } + this.container.appendChild(selectElement); + return this.container; + } +} +class PopupAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.title || parameters.data.contents); + super(parameters, isRenderable); + } + render() { + const IGNORE_TYPES = ['Line']; + this.container.className = 'popupAnnotation'; + if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) { return this.container; } - }); - return PopupAnnotationElement; -}(); -var PopupElement = function PopupElementClosure() { - var BACKGROUND_ENLIGHT = 0.7; - function PopupElement(parameters) { + let selector = '[data-annotation-id="' + this.data.parentId + '"]'; + let parentElement = this.layer.querySelector(selector); + if (!parentElement) { + return this.container; + } + let popup = new PopupElement({ + container: this.container, + trigger: parentElement, + color: this.data.color, + title: this.data.title, + contents: this.data.contents + }); + let parentLeft = parseFloat(parentElement.style.left); + let parentWidth = parseFloat(parentElement.style.width); + _dom_utils.CustomStyle.setProp('transformOrigin', this.container, -(parentLeft + parentWidth) + 'px -' + parentElement.style.top); + this.container.style.left = parentLeft + parentWidth + 'px'; + this.container.appendChild(popup.render()); + return this.container; + } +} +class PopupElement { + constructor(parameters) { this.container = parameters.container; this.trigger = parameters.trigger; this.color = parameters.color; @@ -2136,174 +2107,157 @@ var PopupElement = function PopupElementClosure() { this.hideWrapper = parameters.hideWrapper || false; this.pinned = false; } - PopupElement.prototype = { - render: function PopupElement_render() { - var wrapper = document.createElement('div'); - wrapper.className = 'popupWrapper'; - this.hideElement = this.hideWrapper ? wrapper : this.container; + render() { + const BACKGROUND_ENLIGHT = 0.7; + let wrapper = document.createElement('div'); + wrapper.className = 'popupWrapper'; + this.hideElement = this.hideWrapper ? wrapper : this.container; + this.hideElement.setAttribute('hidden', true); + let popup = document.createElement('div'); + popup.className = 'popup'; + let color = this.color; + if (color) { + let r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0]; + let g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1]; + let b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2]; + popup.style.backgroundColor = _util.Util.makeCssRgb(r | 0, g | 0, b | 0); + } + let contents = this._formatContents(this.contents); + let title = document.createElement('h1'); + title.textContent = this.title; + this.trigger.addEventListener('click', this._toggle.bind(this)); + this.trigger.addEventListener('mouseover', this._show.bind(this, false)); + this.trigger.addEventListener('mouseout', this._hide.bind(this, false)); + popup.addEventListener('click', this._hide.bind(this, true)); + popup.appendChild(title); + popup.appendChild(contents); + wrapper.appendChild(popup); + return wrapper; + } + _formatContents(contents) { + let p = document.createElement('p'); + let lines = contents.split(/(?:\r\n?|\n)/); + for (let i = 0, ii = lines.length; i < ii; ++i) { + let line = lines[i]; + p.appendChild(document.createTextNode(line)); + if (i < ii - 1) { + p.appendChild(document.createElement('br')); + } + } + return p; + } + _toggle() { + if (this.pinned) { + this._hide(true); + } else { + this._show(true); + } + } + _show(pin = false) { + if (pin) { + this.pinned = true; + } + if (this.hideElement.hasAttribute('hidden')) { + this.hideElement.removeAttribute('hidden'); + this.container.style.zIndex += 1; + } + } + _hide(unpin = true) { + if (unpin) { + this.pinned = false; + } + if (!this.hideElement.hasAttribute('hidden') && !this.pinned) { this.hideElement.setAttribute('hidden', true); - var popup = document.createElement('div'); - popup.className = 'popup'; - var color = this.color; - if (color) { - var r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0]; - var g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1]; - var b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2]; - popup.style.backgroundColor = _util.Util.makeCssRgb(r | 0, g | 0, b | 0); - } - var contents = this._formatContents(this.contents); - var title = document.createElement('h1'); - title.textContent = this.title; - this.trigger.addEventListener('click', this._toggle.bind(this)); - this.trigger.addEventListener('mouseover', this._show.bind(this, false)); - this.trigger.addEventListener('mouseout', this._hide.bind(this, false)); - popup.addEventListener('click', this._hide.bind(this, true)); - popup.appendChild(title); - popup.appendChild(contents); - wrapper.appendChild(popup); - return wrapper; - }, - _formatContents: function PopupElement_formatContents(contents) { - var p = document.createElement('p'); - var lines = contents.split(/(?:\r\n?|\n)/); - for (var i = 0, ii = lines.length; i < ii; ++i) { - var line = lines[i]; - p.appendChild(document.createTextNode(line)); - if (i < ii - 1) { - p.appendChild(document.createElement('br')); - } - } - return p; - }, - _toggle: function PopupElement_toggle() { - if (this.pinned) { - this._hide(true); - } else { - this._show(true); - } - }, - _show: function PopupElement_show(pin) { - if (pin) { - this.pinned = true; - } - if (this.hideElement.hasAttribute('hidden')) { - this.hideElement.removeAttribute('hidden'); - this.container.style.zIndex += 1; - } - }, - _hide: function PopupElement_hide(unpin) { - if (unpin) { - this.pinned = false; - } - if (!this.hideElement.hasAttribute('hidden') && !this.pinned) { - this.hideElement.setAttribute('hidden', true); - this.container.style.zIndex -= 1; - } + this.container.style.zIndex -= 1; } - }; - return PopupElement; -}(); -var LineAnnotationElement = function LineAnnotationElementClosure() { - var SVG_NS = 'http://www.w3.org/2000/svg'; - function LineAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); } - _util.Util.inherit(LineAnnotationElement, AnnotationElement, { - render: function LineAnnotationElement_render() { - this.container.className = 'lineAnnotation'; - var data = this.data; - var width = data.rect[2] - data.rect[0]; - var height = data.rect[3] - data.rect[1]; - var svg = document.createElementNS(SVG_NS, 'svg:svg'); - svg.setAttributeNS(null, 'version', '1.1'); - svg.setAttributeNS(null, 'width', width + 'px'); - svg.setAttributeNS(null, 'height', height + 'px'); - svg.setAttributeNS(null, 'preserveAspectRatio', 'none'); - svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height); - var line = document.createElementNS(SVG_NS, 'svg:line'); - line.setAttributeNS(null, 'x1', data.rect[2] - data.lineCoordinates[0]); - line.setAttributeNS(null, 'y1', data.rect[3] - data.lineCoordinates[1]); - line.setAttributeNS(null, 'x2', data.rect[2] - data.lineCoordinates[2]); - line.setAttributeNS(null, 'y2', data.rect[3] - data.lineCoordinates[3]); - line.setAttributeNS(null, 'stroke-width', data.borderStyle.width); - line.setAttributeNS(null, 'stroke', 'transparent'); - svg.appendChild(line); - this.container.append(svg); - this._createPopup(this.container, line, this.data); - return this.container; - } - }); - return LineAnnotationElement; -}(); -var HighlightAnnotationElement = function HighlightAnnotationElementClosure() { - function HighlightAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); +} +class LineAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); } - _util.Util.inherit(HighlightAnnotationElement, AnnotationElement, { - render: function HighlightAnnotationElement_render() { - this.container.className = 'highlightAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - } - }); - return HighlightAnnotationElement; -}(); -var UnderlineAnnotationElement = function UnderlineAnnotationElementClosure() { - function UnderlineAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); + render() { + const SVG_NS = 'http://www.w3.org/2000/svg'; + this.container.className = 'lineAnnotation'; + let data = this.data; + let width = data.rect[2] - data.rect[0]; + let height = data.rect[3] - data.rect[1]; + let svg = document.createElementNS(SVG_NS, 'svg:svg'); + svg.setAttributeNS(null, 'version', '1.1'); + svg.setAttributeNS(null, 'width', width + 'px'); + svg.setAttributeNS(null, 'height', height + 'px'); + svg.setAttributeNS(null, 'preserveAspectRatio', 'none'); + svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height); + let line = document.createElementNS(SVG_NS, 'svg:line'); + line.setAttributeNS(null, 'x1', data.rect[2] - data.lineCoordinates[0]); + line.setAttributeNS(null, 'y1', data.rect[3] - data.lineCoordinates[1]); + line.setAttributeNS(null, 'x2', data.rect[2] - data.lineCoordinates[2]); + line.setAttributeNS(null, 'y2', data.rect[3] - data.lineCoordinates[3]); + line.setAttributeNS(null, 'stroke-width', data.borderStyle.width); + line.setAttributeNS(null, 'stroke', 'transparent'); + svg.appendChild(line); + this.container.append(svg); + this._createPopup(this.container, line, this.data); + return this.container; } - _util.Util.inherit(UnderlineAnnotationElement, AnnotationElement, { - render: function UnderlineAnnotationElement_render() { - this.container.className = 'underlineAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - } - }); - return UnderlineAnnotationElement; -}(); -var SquigglyAnnotationElement = function SquigglyAnnotationElementClosure() { - function SquigglyAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); +} +class HighlightAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); } - _util.Util.inherit(SquigglyAnnotationElement, AnnotationElement, { - render: function SquigglyAnnotationElement_render() { - this.container.className = 'squigglyAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; + render() { + this.container.className = 'highlightAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); } - }); - return SquigglyAnnotationElement; -}(); -var StrikeOutAnnotationElement = function StrikeOutAnnotationElementClosure() { - function StrikeOutAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); + return this.container; } - _util.Util.inherit(StrikeOutAnnotationElement, AnnotationElement, { - render: function StrikeOutAnnotationElement_render() { - this.container.className = 'strikeoutAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; +} +class UnderlineAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); + } + render() { + this.container.className = 'underlineAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); } - }); - return StrikeOutAnnotationElement; -}(); -var FileAttachmentAnnotationElement = function FileAttachmentAnnotationElementClosure() { - function FileAttachmentAnnotationElement(parameters) { - AnnotationElement.call(this, parameters, true); - var file = this.data.file; + return this.container; + } +} +class SquigglyAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); + } + render() { + this.container.className = 'squigglyAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} +class StrikeOutAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); + } + render() { + this.container.className = 'strikeoutAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} +class FileAttachmentAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, true); + let file = this.data.file; this.filename = (0, _dom_utils.getFilenameFromUrl)(file.filename); this.content = file.content; this.linkService.onFileAttachmentAnnotation({ @@ -2312,65 +2266,59 @@ var FileAttachmentAnnotationElement = function FileAttachmentAnnotationElementCl content: file.content }); } - _util.Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, { - render: function FileAttachmentAnnotationElement_render() { - this.container.className = 'fileAttachmentAnnotation'; - var trigger = document.createElement('div'); - trigger.style.height = this.container.style.height; - trigger.style.width = this.container.style.width; - trigger.addEventListener('dblclick', this._download.bind(this)); - if (!this.data.hasPopup && (this.data.title || this.data.contents)) { - this._createPopup(this.container, trigger, this.data); - } - this.container.appendChild(trigger); - return this.container; - }, - _download: function FileAttachmentAnnotationElement_download() { - if (!this.downloadManager) { - (0, _util.warn)('Download cannot be started due to unavailable download manager'); - return; - } - this.downloadManager.downloadData(this.content, this.filename, ''); + render() { + this.container.className = 'fileAttachmentAnnotation'; + let trigger = document.createElement('div'); + trigger.style.height = this.container.style.height; + trigger.style.width = this.container.style.width; + trigger.addEventListener('dblclick', this._download.bind(this)); + if (!this.data.hasPopup && (this.data.title || this.data.contents)) { + this._createPopup(this.container, trigger, this.data); } - }); - return FileAttachmentAnnotationElement; -}(); -var AnnotationLayer = function AnnotationLayerClosure() { - return { - render: function AnnotationLayer_render(parameters) { - var annotationElementFactory = new AnnotationElementFactory(); - for (var i = 0, ii = parameters.annotations.length; i < ii; i++) { - var data = parameters.annotations[i]; - if (!data) { - continue; - } - var element = annotationElementFactory.create({ - data, - layer: parameters.div, - page: parameters.page, - viewport: parameters.viewport, - linkService: parameters.linkService, - downloadManager: parameters.downloadManager, - imageResourcesPath: parameters.imageResourcesPath || (0, _dom_utils.getDefaultSetting)('imageResourcesPath'), - renderInteractiveForms: parameters.renderInteractiveForms || false - }); - if (element.isRenderable) { - parameters.div.appendChild(element.render()); - } - } - }, - update: function AnnotationLayer_update(parameters) { - for (var i = 0, ii = parameters.annotations.length; i < ii; i++) { - var data = parameters.annotations[i]; - var element = parameters.div.querySelector('[data-annotation-id="' + data.id + '"]'); - if (element) { - _dom_utils.CustomStyle.setProp('transform', element, 'matrix(' + parameters.viewport.transform.join(',') + ')'); - } - } - parameters.div.removeAttribute('hidden'); + this.container.appendChild(trigger); + return this.container; + } + _download() { + if (!this.downloadManager) { + (0, _util.warn)('Download cannot be started due to unavailable download manager'); + return; } - }; -}(); + this.downloadManager.downloadData(this.content, this.filename, ''); + } +} +class AnnotationLayer { + static render(parameters) { + for (let i = 0, ii = parameters.annotations.length; i < ii; i++) { + let data = parameters.annotations[i]; + if (!data) { + continue; + } + let element = AnnotationElementFactory.create({ + data, + layer: parameters.div, + page: parameters.page, + viewport: parameters.viewport, + linkService: parameters.linkService, + downloadManager: parameters.downloadManager, + imageResourcesPath: parameters.imageResourcesPath || (0, _dom_utils.getDefaultSetting)('imageResourcesPath'), + renderInteractiveForms: parameters.renderInteractiveForms || false + }); + if (element.isRenderable) { + parameters.div.appendChild(element.render()); + } + } + } + static update(parameters) { + for (let i = 0, ii = parameters.annotations.length; i < ii; i++) { + let data = parameters.annotations[i]; + let element = parameters.div.querySelector('[data-annotation-id="' + data.id + '"]'); + if (element) { + _dom_utils.CustomStyle.setProp('transform', element, 'matrix(' + parameters.viewport.transform.join(',') + ')'); + } + } + parameters.div.removeAttribute('hidden'); + } +} exports.AnnotationLayer = AnnotationLayer; /***/ }), @@ -3846,8 +3794,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() { }(); var version, build; { - exports.version = version = '1.9.489'; - exports.build = build = 'b7fcaff0'; + exports.version = version = '1.9.512'; + exports.build = build = '066fea9c'; } exports.getDocument = getDocument; exports.LoopbackPort = LoopbackPort; @@ -4903,8 +4851,8 @@ if (!_global_scope2.default.PDFJS) { } var PDFJS = _global_scope2.default.PDFJS; { - PDFJS.version = '1.9.489'; - PDFJS.build = 'b7fcaff0'; + PDFJS.version = '1.9.512'; + PDFJS.build = '066fea9c'; } PDFJS.pdfBug = false; if (PDFJS.verbosity !== undefined) { @@ -10470,8 +10418,8 @@ exports.PDFDataTransportStream = PDFDataTransportStream; "use strict"; -var pdfjsVersion = '1.9.489'; -var pdfjsBuild = 'b7fcaff0'; +var pdfjsVersion = '1.9.512'; +var pdfjsBuild = '066fea9c'; var pdfjsSharedUtil = __w_pdfjs_require__(0); var pdfjsDisplayGlobal = __w_pdfjs_require__(9); var pdfjsDisplayAPI = __w_pdfjs_require__(4); diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 7a4513bdff64..76c842d1d12d 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -1023,6 +1023,12 @@ function wrapReason(reason) { return new UnknownErrorException(reason.message, reason.details); } } +function makeReasonSerializable(reason) { + if (!(reason instanceof Error) || reason instanceof AbortException || reason instanceof MissingPDFException || reason instanceof UnexpectedResponseException || reason instanceof UnknownErrorException) { + return reason; + } + return new UnknownErrorException(reason.message, reason.toString()); +} function resolveOrReject(capability, success, reason) { if (success) { capability.resolve(); @@ -1080,15 +1086,12 @@ function MessageHandler(sourceName, targetName, comObj) { data: result }); }, reason => { - if (reason instanceof Error) { - reason = reason + ''; - } comObj.postMessage({ sourceName, targetName, isReply: true, callbackId: data.callbackId, - error: reason + error: makeReasonSerializable(reason) }); }); } else if (data.streamId) { @@ -22651,13 +22654,19 @@ var XRef = function XRefClosure() { var obj1 = parser.getObj(); var obj2 = parser.getObj(); var obj3 = parser.getObj(); - if (!(0, _util.isInt)(obj1) || parseInt(obj1, 10) !== num || !(0, _util.isInt)(obj2) || parseInt(obj2, 10) !== gen || !(0, _primitives.isCmd)(obj3)) { + if (!Number.isInteger(obj1)) { + obj1 = parseInt(obj1, 10); + } + if (!Number.isInteger(obj2)) { + obj2 = parseInt(obj2, 10); + } + if (obj1 !== num || obj2 !== gen || !(0, _primitives.isCmd)(obj3)) { throw new _util.FormatError('bad XRef entry'); } - if (!(0, _primitives.isCmd)(obj3, 'obj')) { + if (obj3.cmd !== 'obj') { if (obj3.cmd.indexOf('obj') === 0) { num = parseInt(obj3.cmd.substring(3), 10); - if (!isNaN(num)) { + if (!Number.isNaN(num)) { return num; } } @@ -27314,17 +27323,16 @@ var _evaluator = __w_pdfjs_require__(13); var _stream = __w_pdfjs_require__(2); -function AnnotationFactory() {} -AnnotationFactory.prototype = { - create: function AnnotationFactory_create(xref, ref, pdfManager, idFactory) { - var dict = xref.fetchIfRef(ref); +class AnnotationFactory { + static create(xref, ref, pdfManager, idFactory) { + let dict = xref.fetchIfRef(ref); if (!(0, _primitives.isDict)(dict)) { return; } - var id = (0, _primitives.isRef)(ref) ? ref.toString() : 'annot_' + idFactory.createObjId(); - var subtype = dict.get('Subtype'); + let id = (0, _primitives.isRef)(ref) ? ref.toString() : 'annot_' + idFactory.createObjId(); + let subtype = dict.get('Subtype'); subtype = (0, _primitives.isName)(subtype) ? subtype.name : null; - var parameters = { + let parameters = { xref, dict, ref: (0, _primitives.isRef)(ref) ? ref : null, @@ -27338,7 +27346,7 @@ AnnotationFactory.prototype = { case 'Text': return new TextAnnotation(parameters); case 'Widget': - var fieldType = _util.Util.getInheritableProperty(dict, 'FT'); + let fieldType = _util.Util.getInheritableProperty(dict, 'FT'); fieldType = (0, _primitives.isName)(fieldType) ? fieldType.name : null; switch (fieldType) { case 'Tx': @@ -27373,278 +27381,273 @@ AnnotationFactory.prototype = { return new Annotation(parameters); } } -}; -var Annotation = function AnnotationClosure() { - function getTransformMatrix(rect, bbox, matrix) { - var bounds = _util.Util.getAxialAlignedBoundingBox(bbox, matrix); - var minX = bounds[0]; - var minY = bounds[1]; - var maxX = bounds[2]; - var maxY = bounds[3]; - if (minX === maxX || minY === maxY) { - return [1, 0, 0, 1, rect[0], rect[1]]; - } - var xRatio = (rect[2] - rect[0]) / (maxX - minX); - var yRatio = (rect[3] - rect[1]) / (maxY - minY); - return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio]; +} +function getTransformMatrix(rect, bbox, matrix) { + let bounds = _util.Util.getAxialAlignedBoundingBox(bbox, matrix); + let minX = bounds[0]; + let minY = bounds[1]; + let maxX = bounds[2]; + let maxY = bounds[3]; + if (minX === maxX || minY === maxY) { + return [1, 0, 0, 1, rect[0], rect[1]]; } - function Annotation(params) { - var dict = params.dict; + let xRatio = (rect[2] - rect[0]) / (maxX - minX); + let yRatio = (rect[3] - rect[1]) / (maxY - minY); + return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio]; +} +class Annotation { + constructor(params) { + let dict = params.dict; this.setFlags(dict.get('F')); this.setRectangle(dict.getArray('Rect')); this.setColor(dict.getArray('C')); this.setBorderStyle(dict); this.setAppearance(dict); - this.data = {}; - this.data.id = params.id; - this.data.subtype = params.subtype; - this.data.annotationFlags = this.flags; - this.data.rect = this.rectangle; - this.data.color = this.color; - this.data.borderStyle = this.borderStyle; - this.data.hasAppearance = !!this.appearance; + this.data = { + annotationFlags: this.flags, + borderStyle: this.borderStyle, + color: this.color, + hasAppearance: !!this.appearance, + id: params.id, + rect: this.rectangle, + subtype: params.subtype + }; } - Annotation.prototype = { - _hasFlag: function Annotation_hasFlag(flags, flag) { - return !!(flags & flag); - }, - _isViewable: function Annotation_isViewable(flags) { - return !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN) && !this._hasFlag(flags, _util.AnnotationFlag.NOVIEW); - }, - _isPrintable: function AnnotationFlag_isPrintable(flags) { - return this._hasFlag(flags, _util.AnnotationFlag.PRINT) && !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN); - }, - get viewable() { - if (this.flags === 0) { - return true; - } - return this._isViewable(this.flags); - }, - get printable() { - if (this.flags === 0) { - return false; - } - return this._isPrintable(this.flags); - }, - setFlags: function Annotation_setFlags(flags) { - this.flags = (0, _util.isInt)(flags) && flags > 0 ? flags : 0; - }, - hasFlag: function Annotation_hasFlag(flag) { - return this._hasFlag(this.flags, flag); - }, - setRectangle: function Annotation_setRectangle(rectangle) { - if ((0, _util.isArray)(rectangle) && rectangle.length === 4) { - this.rectangle = _util.Util.normalizeRect(rectangle); - } else { - this.rectangle = [0, 0, 0, 0]; - } - }, - setColor: function Annotation_setColor(color) { - var rgbColor = new Uint8Array(3); - if (!(0, _util.isArray)(color)) { - this.color = rgbColor; - return; - } - switch (color.length) { - case 0: - this.color = null; - break; - case 1: - _colorspace.ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - case 3: - _colorspace.ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - case 4: - _colorspace.ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - default: - this.color = rgbColor; - break; - } - }, - setBorderStyle: function Annotation_setBorderStyle(borderStyle) { - this.borderStyle = new AnnotationBorderStyle(); - if (!(0, _primitives.isDict)(borderStyle)) { - return; - } - if (borderStyle.has('BS')) { - var dict = borderStyle.get('BS'); - var dictType = dict.get('Type'); - if (!dictType || (0, _primitives.isName)(dictType, 'Border')) { - this.borderStyle.setWidth(dict.get('W')); - this.borderStyle.setStyle(dict.get('S')); - this.borderStyle.setDashArray(dict.getArray('D')); - } - } else if (borderStyle.has('Border')) { - var array = borderStyle.getArray('Border'); - if ((0, _util.isArray)(array) && array.length >= 3) { - this.borderStyle.setHorizontalCornerRadius(array[0]); - this.borderStyle.setVerticalCornerRadius(array[1]); - this.borderStyle.setWidth(array[2]); - if (array.length === 4) { - this.borderStyle.setDashArray(array[3]); - } - } - } else { - this.borderStyle.setWidth(0); - } - }, - setAppearance: function Annotation_setAppearance(dict) { - this.appearance = null; - var appearanceStates = dict.get('AP'); - if (!(0, _primitives.isDict)(appearanceStates)) { - return; - } - var normalAppearanceState = appearanceStates.get('N'); - if ((0, _primitives.isStream)(normalAppearanceState)) { - this.appearance = normalAppearanceState; - return; - } - if (!(0, _primitives.isDict)(normalAppearanceState)) { - return; - } - var as = dict.get('AS'); - if (!(0, _primitives.isName)(as) || !normalAppearanceState.has(as.name)) { - return; - } - this.appearance = normalAppearanceState.get(as.name); - }, - _preparePopup: function Annotation_preparePopup(dict) { - if (!dict.has('C')) { - this.data.color = null; - } - this.data.hasPopup = dict.has('Popup'); - this.data.title = (0, _util.stringToPDFString)(dict.get('T') || ''); - this.data.contents = (0, _util.stringToPDFString)(dict.get('Contents') || ''); - }, - loadResources: function Annotation_loadResources(keys) { - return this.appearance.dict.getAsync('Resources').then(resources => { - if (!resources) { - return; - } - let objectLoader = new _obj.ObjectLoader(resources, keys, resources.xref); - return objectLoader.load().then(function () { - return resources; - }); - }); - }, - getOperatorList: function Annotation_getOperatorList(evaluator, task, renderForms) { - if (!this.appearance) { - return Promise.resolve(new _evaluator.OperatorList()); - } - var data = this.data; - var appearanceDict = this.appearance.dict; - var resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']); - var bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1]; - var matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0]; - var transform = getTransformMatrix(data.rect, bbox, matrix); - return resourcesPromise.then(resources => { - var opList = new _evaluator.OperatorList(); - opList.addOp(_util.OPS.beginAnnotation, [data.rect, transform, matrix]); - return evaluator.getOperatorList({ - stream: this.appearance, - task, - resources, - operatorList: opList - }).then(() => { - opList.addOp(_util.OPS.endAnnotation, []); - this.appearance.reset(); - return opList; - }); - }); + _hasFlag(flags, flag) { + return !!(flags & flag); + } + _isViewable(flags) { + return !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN) && !this._hasFlag(flags, _util.AnnotationFlag.NOVIEW); + } + _isPrintable(flags) { + return this._hasFlag(flags, _util.AnnotationFlag.PRINT) && !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN); + } + get viewable() { + if (this.flags === 0) { + return true; } - }; - return Annotation; -}(); -var AnnotationBorderStyle = function AnnotationBorderStyleClosure() { - function AnnotationBorderStyle() { + return this._isViewable(this.flags); + } + get printable() { + if (this.flags === 0) { + return false; + } + return this._isPrintable(this.flags); + } + setFlags(flags) { + this.flags = (0, _util.isInt)(flags) && flags > 0 ? flags : 0; + } + hasFlag(flag) { + return this._hasFlag(this.flags, flag); + } + setRectangle(rectangle) { + if ((0, _util.isArray)(rectangle) && rectangle.length === 4) { + this.rectangle = _util.Util.normalizeRect(rectangle); + } else { + this.rectangle = [0, 0, 0, 0]; + } + } + setColor(color) { + let rgbColor = new Uint8Array(3); + if (!(0, _util.isArray)(color)) { + this.color = rgbColor; + return; + } + switch (color.length) { + case 0: + this.color = null; + break; + case 1: + _colorspace.ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + case 3: + _colorspace.ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + case 4: + _colorspace.ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + default: + this.color = rgbColor; + break; + } + } + setBorderStyle(borderStyle) { + this.borderStyle = new AnnotationBorderStyle(); + if (!(0, _primitives.isDict)(borderStyle)) { + return; + } + if (borderStyle.has('BS')) { + let dict = borderStyle.get('BS'); + let dictType = dict.get('Type'); + if (!dictType || (0, _primitives.isName)(dictType, 'Border')) { + this.borderStyle.setWidth(dict.get('W')); + this.borderStyle.setStyle(dict.get('S')); + this.borderStyle.setDashArray(dict.getArray('D')); + } + } else if (borderStyle.has('Border')) { + let array = borderStyle.getArray('Border'); + if ((0, _util.isArray)(array) && array.length >= 3) { + this.borderStyle.setHorizontalCornerRadius(array[0]); + this.borderStyle.setVerticalCornerRadius(array[1]); + this.borderStyle.setWidth(array[2]); + if (array.length === 4) { + this.borderStyle.setDashArray(array[3]); + } + } + } else { + this.borderStyle.setWidth(0); + } + } + setAppearance(dict) { + this.appearance = null; + let appearanceStates = dict.get('AP'); + if (!(0, _primitives.isDict)(appearanceStates)) { + return; + } + let normalAppearanceState = appearanceStates.get('N'); + if ((0, _primitives.isStream)(normalAppearanceState)) { + this.appearance = normalAppearanceState; + return; + } + if (!(0, _primitives.isDict)(normalAppearanceState)) { + return; + } + let as = dict.get('AS'); + if (!(0, _primitives.isName)(as) || !normalAppearanceState.has(as.name)) { + return; + } + this.appearance = normalAppearanceState.get(as.name); + } + _preparePopup(dict) { + if (!dict.has('C')) { + this.data.color = null; + } + this.data.hasPopup = dict.has('Popup'); + this.data.title = (0, _util.stringToPDFString)(dict.get('T') || ''); + this.data.contents = (0, _util.stringToPDFString)(dict.get('Contents') || ''); + } + loadResources(keys) { + return this.appearance.dict.getAsync('Resources').then(resources => { + if (!resources) { + return; + } + let objectLoader = new _obj.ObjectLoader(resources, keys, resources.xref); + return objectLoader.load().then(function () { + return resources; + }); + }); + } + getOperatorList(evaluator, task, renderForms) { + if (!this.appearance) { + return Promise.resolve(new _evaluator.OperatorList()); + } + let data = this.data; + let appearanceDict = this.appearance.dict; + let resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']); + let bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1]; + let matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0]; + let transform = getTransformMatrix(data.rect, bbox, matrix); + return resourcesPromise.then(resources => { + let opList = new _evaluator.OperatorList(); + opList.addOp(_util.OPS.beginAnnotation, [data.rect, transform, matrix]); + return evaluator.getOperatorList({ + stream: this.appearance, + task, + resources, + operatorList: opList + }).then(() => { + opList.addOp(_util.OPS.endAnnotation, []); + this.appearance.reset(); + return opList; + }); + }); + } +} +class AnnotationBorderStyle { + constructor() { this.width = 1; this.style = _util.AnnotationBorderStyleType.SOLID; this.dashArray = [3]; this.horizontalCornerRadius = 0; this.verticalCornerRadius = 0; } - AnnotationBorderStyle.prototype = { - setWidth: function AnnotationBorderStyle_setWidth(width) { - if (width === (width | 0)) { - this.width = width; - } - }, - setStyle: function AnnotationBorderStyle_setStyle(style) { - if (!style) { - return; - } - switch (style.name) { - case 'S': - this.style = _util.AnnotationBorderStyleType.SOLID; + setWidth(width) { + if (width === (width | 0)) { + this.width = width; + } + } + setStyle(style) { + if (!style) { + return; + } + switch (style.name) { + case 'S': + this.style = _util.AnnotationBorderStyleType.SOLID; + break; + case 'D': + this.style = _util.AnnotationBorderStyleType.DASHED; + break; + case 'B': + this.style = _util.AnnotationBorderStyleType.BEVELED; + break; + case 'I': + this.style = _util.AnnotationBorderStyleType.INSET; + break; + case 'U': + this.style = _util.AnnotationBorderStyleType.UNDERLINE; + break; + default: + break; + } + } + setDashArray(dashArray) { + if ((0, _util.isArray)(dashArray) && dashArray.length > 0) { + let isValid = true; + let allZeros = true; + for (let i = 0, len = dashArray.length; i < len; i++) { + let element = dashArray[i]; + let validNumber = +element >= 0; + if (!validNumber) { + isValid = false; break; - case 'D': - this.style = _util.AnnotationBorderStyleType.DASHED; - break; - case 'B': - this.style = _util.AnnotationBorderStyleType.BEVELED; - break; - case 'I': - this.style = _util.AnnotationBorderStyleType.INSET; - break; - case 'U': - this.style = _util.AnnotationBorderStyleType.UNDERLINE; - break; - default: - break; - } - }, - setDashArray: function AnnotationBorderStyle_setDashArray(dashArray) { - if ((0, _util.isArray)(dashArray) && dashArray.length > 0) { - var isValid = true; - var allZeros = true; - for (var i = 0, len = dashArray.length; i < len; i++) { - var element = dashArray[i]; - var validNumber = +element >= 0; - if (!validNumber) { - isValid = false; - break; - } else if (element > 0) { - allZeros = false; - } + } else if (element > 0) { + allZeros = false; } - if (isValid && !allZeros) { - this.dashArray = dashArray; - } else { - this.width = 0; - } - } else if (dashArray) { + } + if (isValid && !allZeros) { + this.dashArray = dashArray; + } else { this.width = 0; } - }, - setHorizontalCornerRadius: function AnnotationBorderStyle_setHorizontalCornerRadius(radius) { - if (radius === (radius | 0)) { - this.horizontalCornerRadius = radius; - } - }, - setVerticalCornerRadius: function AnnotationBorderStyle_setVerticalCornerRadius(radius) { - if (radius === (radius | 0)) { - this.verticalCornerRadius = radius; - } + } else if (dashArray) { + this.width = 0; } - }; - return AnnotationBorderStyle; -}(); -var WidgetAnnotation = function WidgetAnnotationClosure() { - function WidgetAnnotation(params) { - Annotation.call(this, params); - var dict = params.dict; - var data = this.data; + } + setHorizontalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.horizontalCornerRadius = radius; + } + } + setVerticalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.verticalCornerRadius = radius; + } + } +} +class WidgetAnnotation extends Annotation { + constructor(params) { + super(params); + let dict = params.dict; + let data = this.data; data.annotationType = _util.AnnotationType.WIDGET; data.fieldName = this._constructFieldName(dict); data.fieldValue = _util.Util.getInheritableProperty(dict, 'V', true); data.alternativeText = (0, _util.stringToPDFString)(dict.get('TU') || ''); data.defaultAppearance = _util.Util.getInheritableProperty(dict, 'DA') || ''; - var fieldType = _util.Util.getInheritableProperty(dict, 'FT'); + let fieldType = _util.Util.getInheritableProperty(dict, 'FT'); data.fieldType = (0, _primitives.isName)(fieldType) ? fieldType.name : null; this.fieldResources = _util.Util.getInheritableProperty(dict, 'DR') || _primitives.Dict.empty; data.fieldFlags = _util.Util.getInheritableProperty(dict, 'Ff'); @@ -27656,47 +27659,50 @@ var WidgetAnnotation = function WidgetAnnotationClosure() { this.setFlags(_util.AnnotationFlag.HIDDEN); } } - _util.Util.inherit(WidgetAnnotation, Annotation, { - _constructFieldName: function WidgetAnnotation_constructFieldName(dict) { - if (!dict.has('T') && !dict.has('Parent')) { - (0, _util.warn)('Unknown field name, falling back to empty field name.'); - return ''; - } - if (!dict.has('Parent')) { - return (0, _util.stringToPDFString)(dict.get('T')); - } - var fieldName = []; - if (dict.has('T')) { - fieldName.unshift((0, _util.stringToPDFString)(dict.get('T'))); - } - var loopDict = dict; - while (loopDict.has('Parent')) { - loopDict = loopDict.get('Parent'); - if (!(0, _primitives.isDict)(loopDict)) { - break; - } - if (loopDict.has('T')) { - fieldName.unshift((0, _util.stringToPDFString)(loopDict.get('T'))); - } - } - return fieldName.join('.'); - }, - hasFieldFlag: function WidgetAnnotation_hasFieldFlag(flag) { - return !!(this.data.fieldFlags & flag); + _constructFieldName(dict) { + if (!dict.has('T') && !dict.has('Parent')) { + (0, _util.warn)('Unknown field name, falling back to empty field name.'); + return ''; } - }); - return WidgetAnnotation; -}(); -var TextWidgetAnnotation = function TextWidgetAnnotationClosure() { - function TextWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); + if (!dict.has('Parent')) { + return (0, _util.stringToPDFString)(dict.get('T')); + } + let fieldName = []; + if (dict.has('T')) { + fieldName.unshift((0, _util.stringToPDFString)(dict.get('T'))); + } + let loopDict = dict; + while (loopDict.has('Parent')) { + loopDict = loopDict.get('Parent'); + if (!(0, _primitives.isDict)(loopDict)) { + break; + } + if (loopDict.has('T')) { + fieldName.unshift((0, _util.stringToPDFString)(loopDict.get('T'))); + } + } + return fieldName.join('.'); + } + hasFieldFlag(flag) { + return !!(this.data.fieldFlags & flag); + } + getOperatorList(evaluator, task, renderForms) { + if (renderForms) { + return Promise.resolve(new _evaluator.OperatorList()); + } + return super.getOperatorList(evaluator, task, renderForms); + } +} +class TextWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); this.data.fieldValue = (0, _util.stringToPDFString)(this.data.fieldValue || ''); - var alignment = _util.Util.getInheritableProperty(params.dict, 'Q'); + let alignment = _util.Util.getInheritableProperty(params.dict, 'Q'); if (!(0, _util.isInt)(alignment) || alignment < 0 || alignment > 2) { alignment = null; } this.data.textAlignment = alignment; - var maximumLength = _util.Util.getInheritableProperty(params.dict, 'MaxLen'); + let maximumLength = _util.Util.getInheritableProperty(params.dict, 'MaxLen'); if (!(0, _util.isInt)(maximumLength) || maximumLength < 0) { maximumLength = null; } @@ -27704,34 +27710,28 @@ var TextWidgetAnnotation = function TextWidgetAnnotationClosure() { this.data.multiLine = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE); this.data.comb = this.hasFieldFlag(_util.AnnotationFieldFlag.COMB) && !this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PASSWORD) && !this.hasFieldFlag(_util.AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== null; } - _util.Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator, task, renderForms) { - var operatorList = new _evaluator.OperatorList(); - if (renderForms) { - return Promise.resolve(operatorList); - } - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms); - } - if (!this.data.defaultAppearance) { - return Promise.resolve(operatorList); - } - var stream = new _stream.Stream((0, _util.stringToBytes)(this.data.defaultAppearance)); - return evaluator.getOperatorList({ - stream, - task, - resources: this.fieldResources, - operatorList - }).then(function () { - return operatorList; - }); + getOperatorList(evaluator, task, renderForms) { + if (renderForms || this.appearance) { + return super.getOperatorList(evaluator, task, renderForms); } - }); - return TextWidgetAnnotation; -}(); -var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() { - function ButtonWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); + let operatorList = new _evaluator.OperatorList(); + if (!this.data.defaultAppearance) { + return Promise.resolve(operatorList); + } + let stream = new _stream.Stream((0, _util.stringToBytes)(this.data.defaultAppearance)); + return evaluator.getOperatorList({ + stream, + task, + resources: this.fieldResources, + operatorList + }).then(function () { + return operatorList; + }); + } +} +class ButtonWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); this.data.checkBox = !this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON); if (this.data.checkBox) { if (!(0, _primitives.isName)(this.data.fieldValue)) { @@ -27742,23 +27742,23 @@ var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() { this.data.radioButton = this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON); if (this.data.radioButton) { this.data.fieldValue = this.data.buttonValue = null; - var fieldParent = params.dict.get('Parent'); + let fieldParent = params.dict.get('Parent'); if ((0, _primitives.isDict)(fieldParent) && fieldParent.has('V')) { - var fieldParentValue = fieldParent.get('V'); + let fieldParentValue = fieldParent.get('V'); if ((0, _primitives.isName)(fieldParentValue)) { this.data.fieldValue = fieldParentValue.name; } } - var appearanceStates = params.dict.get('AP'); + let appearanceStates = params.dict.get('AP'); if (!(0, _primitives.isDict)(appearanceStates)) { return; } - var normalAppearanceState = appearanceStates.get('N'); + let normalAppearanceState = appearanceStates.get('N'); if (!(0, _primitives.isDict)(normalAppearanceState)) { return; } - var keys = normalAppearanceState.getKeys(); - for (var i = 0, ii = keys.length; i < ii; i++) { + let keys = normalAppearanceState.getKeys(); + for (let i = 0, ii = keys.length; i < ii; i++) { if (keys[i] !== 'Off') { this.data.buttonValue = keys[i]; break; @@ -27766,30 +27766,17 @@ var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() { } } } - _util.Util.inherit(ButtonWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function ButtonWidgetAnnotation_getOperatorList(evaluator, task, renderForms) { - var operatorList = new _evaluator.OperatorList(); - if (renderForms) { - return Promise.resolve(operatorList); - } - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms); - } - return Promise.resolve(operatorList); - } - }); - return ButtonWidgetAnnotation; -}(); -var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() { - function ChoiceWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); +} +class ChoiceWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); this.data.options = []; - var options = _util.Util.getInheritableProperty(params.dict, 'Opt'); + let options = _util.Util.getInheritableProperty(params.dict, 'Opt'); if ((0, _util.isArray)(options)) { - var xref = params.xref; - for (var i = 0, ii = options.length; i < ii; i++) { - var option = xref.fetchIfRef(options[i]); - var isOptionArray = (0, _util.isArray)(option); + let xref = params.xref; + for (let i = 0, ii = options.length; i < ii; i++) { + let option = xref.fetchIfRef(options[i]); + let isOptionArray = (0, _util.isArray)(option); this.data.options[i] = { exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option, displayValue: isOptionArray ? xref.fetchIfRef(option[1]) : option @@ -27802,21 +27789,11 @@ var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() { this.data.combo = this.hasFieldFlag(_util.AnnotationFieldFlag.COMBO); this.data.multiSelect = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTISELECT); } - _util.Util.inherit(ChoiceWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function ChoiceWidgetAnnotation_getOperatorList(evaluator, task, renderForms) { - var operatorList = new _evaluator.OperatorList(); - if (renderForms) { - return Promise.resolve(operatorList); - } - return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms); - } - }); - return ChoiceWidgetAnnotation; -}(); -var TextAnnotation = function TextAnnotationClosure() { - var DEFAULT_ICON_SIZE = 22; - function TextAnnotation(parameters) { - Annotation.call(this, parameters); +} +class TextAnnotation extends Annotation { + constructor(parameters) { + const DEFAULT_ICON_SIZE = 22; + super(parameters); this.data.annotationType = _util.AnnotationType.TEXT; if (this.data.hasAppearance) { this.data.name = 'NoIcon'; @@ -27827,34 +27804,29 @@ var TextAnnotation = function TextAnnotationClosure() { } this._preparePopup(parameters.dict); } - _util.Util.inherit(TextAnnotation, Annotation, {}); - return TextAnnotation; -}(); -var LinkAnnotation = function LinkAnnotationClosure() { - function LinkAnnotation(params) { - Annotation.call(this, params); - var data = this.data; - data.annotationType = _util.AnnotationType.LINK; +} +class LinkAnnotation extends Annotation { + constructor(params) { + super(params); + this.data.annotationType = _util.AnnotationType.LINK; _obj.Catalog.parseDestDictionary({ destDict: params.dict, - resultObj: data, + resultObj: this.data, docBaseUrl: params.pdfManager.docBaseUrl }); } - _util.Util.inherit(LinkAnnotation, Annotation, {}); - return LinkAnnotation; -}(); -var PopupAnnotation = function PopupAnnotationClosure() { - function PopupAnnotation(parameters) { - Annotation.call(this, parameters); +} +class PopupAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.POPUP; - var dict = parameters.dict; - var parentItem = dict.get('Parent'); + let dict = parameters.dict; + let parentItem = dict.get('Parent'); if (!parentItem) { (0, _util.warn)('Popup annotation has a missing or invalid parent annotation.'); return; } - var parentSubtype = parentItem.get('Subtype'); + let parentSubtype = parentItem.get('Subtype'); this.data.parentType = (0, _primitives.isName)(parentSubtype) ? parentSubtype.name : null; this.data.parentId = dict.getRaw('Parent').toString(); this.data.title = (0, _util.stringToPDFString)(parentItem.get('T') || ''); @@ -27866,73 +27838,59 @@ var PopupAnnotation = function PopupAnnotationClosure() { this.data.color = this.color; } if (!this.viewable) { - var parentFlags = parentItem.get('F'); + let parentFlags = parentItem.get('F'); if (this._isViewable(parentFlags)) { this.setFlags(parentFlags); } } } - _util.Util.inherit(PopupAnnotation, Annotation, {}); - return PopupAnnotation; -}(); -var LineAnnotation = function LineAnnotationClosure() { - function LineAnnotation(parameters) { - Annotation.call(this, parameters); +} +class LineAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.LINE; - var dict = parameters.dict; + let dict = parameters.dict; this.data.lineCoordinates = _util.Util.normalizeRect(dict.getArray('L')); this._preparePopup(dict); } - _util.Util.inherit(LineAnnotation, Annotation, {}); - return LineAnnotation; -}(); -var HighlightAnnotation = function HighlightAnnotationClosure() { - function HighlightAnnotation(parameters) { - Annotation.call(this, parameters); +} +class HighlightAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.HIGHLIGHT; this._preparePopup(parameters.dict); } - _util.Util.inherit(HighlightAnnotation, Annotation, {}); - return HighlightAnnotation; -}(); -var UnderlineAnnotation = function UnderlineAnnotationClosure() { - function UnderlineAnnotation(parameters) { - Annotation.call(this, parameters); +} +class UnderlineAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.UNDERLINE; this._preparePopup(parameters.dict); } - _util.Util.inherit(UnderlineAnnotation, Annotation, {}); - return UnderlineAnnotation; -}(); -var SquigglyAnnotation = function SquigglyAnnotationClosure() { - function SquigglyAnnotation(parameters) { - Annotation.call(this, parameters); +} +class SquigglyAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.SQUIGGLY; this._preparePopup(parameters.dict); } - _util.Util.inherit(SquigglyAnnotation, Annotation, {}); - return SquigglyAnnotation; -}(); -var StrikeOutAnnotation = function StrikeOutAnnotationClosure() { - function StrikeOutAnnotation(parameters) { - Annotation.call(this, parameters); +} +class StrikeOutAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.STRIKEOUT; this._preparePopup(parameters.dict); } - _util.Util.inherit(StrikeOutAnnotation, Annotation, {}); - return StrikeOutAnnotation; -}(); -var FileAttachmentAnnotation = function FileAttachmentAnnotationClosure() { - function FileAttachmentAnnotation(parameters) { - Annotation.call(this, parameters); - var file = new _obj.FileSpec(parameters.dict.get('FS'), parameters.xref); +} +class FileAttachmentAnnotation extends Annotation { + constructor(parameters) { + super(parameters); + let file = new _obj.FileSpec(parameters.dict.get('FS'), parameters.xref); this.data.annotationType = _util.AnnotationType.FILEATTACHMENT; this.data.file = file.serializable; this._preparePopup(parameters.dict); } - _util.Util.inherit(FileAttachmentAnnotation, Annotation, {}); - return FileAttachmentAnnotation; -}(); +} exports.Annotation = Annotation; exports.AnnotationBorderStyle = AnnotationBorderStyle; exports.AnnotationFactory = AnnotationFactory; @@ -29166,10 +29124,9 @@ var Page = function PageClosure() { get annotations() { var annotations = []; var annotationRefs = this.getInheritedPageProp('Annots') || []; - var annotationFactory = new _annotation.AnnotationFactory(); for (var i = 0, n = annotationRefs.length; i < n; ++i) { var annotationRef = annotationRefs[i]; - var annotation = annotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory); + var annotation = _annotation.AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory); if (annotation) { annotations.push(annotation); } @@ -40063,8 +40020,8 @@ exports.Type1Parser = Type1Parser; "use strict"; -var pdfjsVersion = '1.9.489'; -var pdfjsBuild = 'b7fcaff0'; +var pdfjsVersion = '1.9.512'; +var pdfjsBuild = '066fea9c'; var pdfjsCoreWorker = __w_pdfjs_require__(17); exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler; diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index 352f8b6cee8e..8d733d793d0c 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -1323,7 +1323,9 @@ let PDFViewerApplication = { } } } - console.error(message + '\n' + moreInfoText); + Promise.all(moreInfoText).then(parts => { + console.error(message + '\n' + parts.join('\n')); + }); this.fallback(); }, progress(level) { @@ -4916,7 +4918,6 @@ class PDFPageView { this.reset(); if (this.pdfPage) { this.pdfPage.cleanup(); - this.pdfPage = null; } } _resetZoomLayer(removeFromDOM = false) { diff --git a/config/check_vanilla_allocations.py b/config/check_vanilla_allocations.py index 2a3be879ec89..8c8efd545fdf 100644 --- a/config/check_vanilla_allocations.py +++ b/config/check_vanilla_allocations.py @@ -128,6 +128,12 @@ def main(): continue filename = m.group(1) + + # mozalloc contains calls to memalign. These are ok, so we whitelist + # them. + if "mozalloc" in filename: + continue + fn = m.group(2) if filename == 'jsutil.o': jsutil_cpp.add(fn) @@ -188,4 +194,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/devtools/client/aboutdebugging/aboutdebugging.css b/devtools/client/aboutdebugging/aboutdebugging.css index 038d42188752..5c28bf7d8cea 100644 --- a/devtools/client/aboutdebugging/aboutdebugging.css +++ b/devtools/client/aboutdebugging/aboutdebugging.css @@ -63,6 +63,7 @@ button { .target-icon { height: 24px; + width: 24px; margin-inline-end: 5px; } diff --git a/devtools/client/responsive.html/test/browser/browser_exit_button.js b/devtools/client/responsive.html/test/browser/browser_exit_button.js index 62e652274826..d2a7e6c332f8 100644 --- a/devtools/client/responsive.html/test/browser/browser_exit_button.js +++ b/devtools/client/responsive.html/test/browser/browser_exit_button.js @@ -26,8 +26,9 @@ add_task(function* () { // Detach the tab with RDM open. let newWindow = gBrowser.replaceTabWithWindow(tab); - // Waiting the tab is detached. + // Wait until the tab is detached and the new window is fully initialized. yield waitTabIsDetached; + yield newWindow.delayedStartupPromise; // Get the new tab instance. tab = newWindow.gBrowser.tabs[0]; diff --git a/devtools/client/themes/common.css b/devtools/client/themes/common.css index 626b2fe3ffa5..50c53e8cf8f7 100644 --- a/devtools/client/themes/common.css +++ b/devtools/client/themes/common.css @@ -8,7 +8,7 @@ :root { font: message-box; - --tab-line-selected-color: highlight; + --tab-line-selected-color: var(--blue-50); } :root.theme-light { diff --git a/devtools/client/themes/toolbox.css b/devtools/client/themes/toolbox.css index ade315af355a..9e7fcbeecf61 100644 --- a/devtools/client/themes/toolbox.css +++ b/devtools/client/themes/toolbox.css @@ -106,6 +106,7 @@ } .devtools-tab-label { + font-size: 12px; mask-image: linear-gradient(to left, transparent 0, black 6px); /* Set the end padding on the label to make sure the label gets faded out properly */ padding-inline-end: 10px; diff --git a/devtools/server/actors/accessibility.js b/devtools/server/actors/accessibility.js new file mode 100644 index 000000000000..45e5ddbf4cf6 --- /dev/null +++ b/devtools/server/actors/accessibility.js @@ -0,0 +1,538 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Cc, Ci, Cu } = require("chrome"); +const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const Services = require("Services"); +const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol"); +const defer = require("devtools/shared/defer"); +const events = require("devtools/shared/event-emitter"); +const { + accessibleSpec, + accessibleWalkerSpec, + accessibilitySpec +} = require("devtools/shared/specs/accessibility"); + +const nsIAccessibleEvent = Ci.nsIAccessibleEvent; +const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent; +const nsIPropertyElement = Ci.nsIPropertyElement; + +const { + EVENT_TEXT_CHANGED, + EVENT_TEXT_INSERTED, + EVENT_TEXT_REMOVED, + EVENT_ACCELERATOR_CHANGE, + EVENT_ACTION_CHANGE, + EVENT_DEFACTION_CHANGE, + EVENT_DESCRIPTION_CHANGE, + EVENT_DOCUMENT_ATTRIBUTES_CHANGED, + EVENT_HELP_CHANGE, + EVENT_HIDE, + EVENT_NAME_CHANGE, + EVENT_OBJECT_ATTRIBUTE_CHANGED, + EVENT_REORDER, + EVENT_STATE_CHANGE, + EVENT_TEXT_ATTRIBUTE_CHANGED, + EVENT_VALUE_CHANGE +} = nsIAccessibleEvent; + +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +/** + * Set of actors that expose accessibility tree information to the + * devtools protocol clients. + * + * The |Accessibility| actor is the main entry point. It is used to request + * an AccessibleWalker actor that caches the tree of Accessible actors. + * + * The |AccessibleWalker| actor is used to cache all seen Accessible actors as + * well as observe all relevant accesible events. + * + * The |Accessible| actor provides information about a particular accessible + * object, its properties, , attributes, states, relations, etc. + */ + +/** + * The AccessibleActor provides information about a given accessible object: its + * role, name, states, etc. + */ +const AccessibleActor = ActorClassWithSpec(accessibleSpec, { + initialize(walker, rawAccessible) { + Actor.prototype.initialize.call(this, walker.conn); + this.walker = walker; + this.rawAccessible = rawAccessible; + + /** + * Indicates if the raw accessible is no longer alive. + * + * @return Boolean + */ + Object.defineProperty(this, "isDefunct", { + get() { + let defunct = false; + + try { + let extState = {}; + this.rawAccessible.getState({}, extState); + // extState.value is a bitmask. We are applying bitwise AND to mask out + // irrelelvant states. + defunct = !!(extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT); + } catch (e) { + defunct = true; + } + + if (defunct) { + delete this.isDefunct; + this.isDefunct = true; + return this.isDefunct; + } + + return defunct; + }, + configurable: true + }); + }, + + /** + * Items returned by this actor should belong to the parent walker. + */ + marshallPool() { + return this.walker; + }, + + destroy() { + Actor.prototype.destroy.call(this); + this.walker = null; + this.rawAccessible = null; + }, + + get role() { + if (this.isDefunct) { + return null; + } + return this.walker.a11yService.getStringRole(this.rawAccessible.role); + }, + + get name() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.name; + }, + + get value() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.value; + }, + + get description() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.description; + }, + + get help() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.help; + }, + + get keyboardShortcut() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.keyboardShortcut; + }, + + get childCount() { + if (this.isDefunct) { + return 0; + } + return this.rawAccessible.childCount; + }, + + get domNodeType() { + if (this.isDefunct) { + return 0; + } + return this.rawAccessible.DOMNode ? this.rawAccessible.DOMNode.nodeType : 0; + }, + + children() { + let children = []; + if (this.isDefunct) { + return children; + } + + for (let child = this.rawAccessible.firstChild; child; child = child.nextSibling) { + children.push(this.walker.addRef(child)); + } + return children; + }, + + getIndexInParent() { + if (this.isDefunct) { + return -1; + } + return this.rawAccessible.indexInParent; + }, + + getActions() { + let actions = []; + if (this.isDefunct) { + return actions; + } + + for (let i = 0; i < this.rawAccessible.actionCount; i++) { + actions.push(this.rawAccessible.getActionDescription(i)); + } + return actions; + }, + + getState() { + if (this.isDefunct) { + return []; + } + + let state = {}; + let extState = {}; + this.rawAccessible.getState(state, extState); + return [ + ...this.walker.a11yService.getStringStates(state.value, extState.value) + ]; + }, + + getAttributes() { + if (this.isDefunct || !this.rawAccessible.attributes) { + return {}; + } + + let attributes = {}; + let attrsEnum = this.rawAccessible.attributes.enumerate(); + while (attrsEnum.hasMoreElements()) { + let { key, value } = attrsEnum.getNext().QueryInterface( + nsIPropertyElement); + attributes[key] = value; + } + + return attributes; + }, + + form() { + return { + actor: this.actorID, + role: this.role, + name: this.name, + value: this.value, + description: this.description, + help: this.help, + keyboardShortcut: this.keyboardShortcut, + childCount: this.childCount, + domNodeType: this.domNodeType, + walker: this.walker.form() + }; + } +}); + +/** + * The AccessibleWalkerActor stores a cache of AccessibleActors that represent + * accessible objects in a given document. + * + * It is also responsible for implicitely initializing and shutting down + * accessibility engine by storing a reference to the XPCOM accessibility + * service. + */ +const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { + initialize(conn, tabActor) { + Actor.prototype.initialize.call(this, conn); + this.tabActor = tabActor; + this.rootWin = tabActor.window; + this.rootDoc = tabActor.window.document; + this.refMap = new Map(); + // Accessibility Walker should only be considered ready, when raw accessible + // object for root document is fully initialized (e.g. does not have a + // 'busy' state) + this.readyDeferred = defer(); + + DevToolsUtils.defineLazyGetter(this, "a11yService", () => { + Services.obs.addObserver(this, "accessible-event"); + return Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService); + }); + + this.onLoad = this.onLoad.bind(this); + this.onUnload = this.onUnload.bind(this); + + events.on(tabActor, "will-navigate", this.onUnload); + events.on(tabActor, "window-ready", this.onLoad); + }, + + onUnload({ window }) { + let doc = window.document; + let actor = this.getRef(doc); + + // If an accessible actor was never created for document, then there's + // nothing to clean up. + if (!actor) { + return; + } + + // Purge document's subtree from accessible actors cache. + this.purgeSubtree(this.a11yService.getAccessibleFor(this.doc)); + // If document is a root document, clear it's reference and cache. + if (this.rootDoc === doc) { + this.rootDoc = null; + this.refMap.clear(); + this.readyDeferred = defer(); + } + }, + + onLoad({ window, isTopLevel }) { + if (isTopLevel) { + // If root document is dead, unload it and clean up. + if (this.rootDoc && !Cu.isDeadWrapper(this.rootDoc) && + this.rootDoc.defaultView) { + this.onUnload({ window: this.rootDoc.defaultView }); + } + + this.rootWin = window; + this.rootDoc = window.document; + } + }, + + destroy() { + if (this._destroyed) { + return; + } + + this._destroyed = true; + + try { + Services.obs.removeObserver(this, "accessible-event"); + } catch (e) { + // Accessible event observer might not have been initialized if a11y + // service was never used. + } + + // Clean up accessible actors cache. + if (this.refMap.size > 0) { + this.purgeSubtree(this.a11yService.getAccessibleFor(this.rootDoc)); + this.refMap.clear(); + } + + events.off(this.tabActor, "will-navigate", this.onUnload); + events.off(this.tabActor, "window-ready", this.onLoad); + + this.onLoad = null; + this.onUnload = null; + delete this.a11yService; + this.tabActor = null; + this.rootDoc = null; + this.refMap = null; + + Actor.prototype.destroy.call(this); + }, + + getRef(rawAccessible) { + return this.refMap.get(rawAccessible); + }, + + addRef(rawAccessible) { + let actor = this.refMap.get(rawAccessible); + if (actor) { + return actor; + } + + actor = new AccessibleActor(this, rawAccessible); + this.manage(actor); + this.refMap.set(rawAccessible, actor); + + return actor; + }, + + /** + * Clean up accessible actors cache for a given accessible's subtree. + * + * @param {nsIAccessible} rawAccessible + */ + purgeSubtree(rawAccessible) { + let actor = this.getRef(rawAccessible); + if (actor && rawAccessible && !actor.isDefunct) { + for (let child = rawAccessible.firstChild; child; child = child.nextSibling) { + this.purgeSubtree(child); + } + } + + this.refMap.delete(rawAccessible); + + if (actor) { + events.emit(this, "accessible-destroy", actor); + actor.destroy(); + } + }, + + /** + * A helper method. Accessibility walker is assumed to have only 1 child which + * is the top level document. + */ + children() { + return Promise.all([this.getDocument()]); + }, + + /** + * A promise for a root document accessible actor that only resolves when its + * corresponding document accessible object is fully loaded. + * + * @return {Promise} + */ + getDocument() { + let doc = this.addRef(this.a11yService.getAccessibleFor(this.rootDoc)); + let states = doc.getState(); + + if (states.includes("busy")) { + return this.readyDeferred.promise.then(() => doc); + } + + this.readyDeferred.resolve(); + return Promise.resolve(doc); + }, + + getAccessibleFor(domNode) { + // We need to make sure that the document is loaded processed by a11y first. + return this.getDocument().then(() => + this.addRef(this.a11yService.getAccessibleFor(domNode.rawNode))); + }, + + /** + * Accessible event observer function. + * + * @param {nsIAccessibleEvent} subject + * accessible event object. + */ + observe(subject) { + let event = subject.QueryInterface(nsIAccessibleEvent); + let rawAccessible = event.accessible; + let accessible = this.getRef(rawAccessible); + + switch (event.eventType) { + case EVENT_STATE_CHANGE: + let { state, isEnabled } = event.QueryInterface(nsIAccessibleStateChangeEvent); + let states = [...this.a11yService.getStringStates(state, 0)]; + + if (states.includes("busy") && !isEnabled) { + let { DOMNode } = event; + // If debugging chrome, wait for top level content document loaded, + // otherwise wait for root document loaded. + if (DOMNode == this.rootDoc || ( + this.rootDoc.documentElement.namespaceURI === XUL_NS && + this.rootWin.gBrowser.selectedBrowser.contentDocument == DOMNode)) { + this.readyDeferred.resolve(); + } + } + + if (accessible) { + // Only propagate state change events for active accessibles. + if (states.includes("busy") && isEnabled) { + return; + } + events.emit(accessible, "state-change", accessible.getState()); + } + + break; + case EVENT_NAME_CHANGE: + if (accessible) { + events.emit(accessible, "name-change", rawAccessible.name, + event.DOMNode == this.rootDoc ? + undefined : this.getRef(rawAccessible.parent)); + } + break; + case EVENT_VALUE_CHANGE: + if (accessible) { + events.emit(accessible, "value-change", rawAccessible.value); + } + break; + case EVENT_DESCRIPTION_CHANGE: + if (accessible) { + events.emit(accessible, "description-change", rawAccessible.description); + } + break; + case EVENT_HELP_CHANGE: + if (accessible) { + events.emit(accessible, "help-change", rawAccessible.help); + } + break; + case EVENT_REORDER: + if (accessible) { + events.emit(accessible, "reorder", rawAccessible.childCount); + } + break; + case EVENT_HIDE: + this.purgeSubtree(rawAccessible); + break; + case EVENT_DEFACTION_CHANGE: + case EVENT_ACTION_CHANGE: + if (accessible) { + events.emit(accessible, "actions-change", accessible.getActions()); + } + break; + case EVENT_TEXT_CHANGED: + case EVENT_TEXT_INSERTED: + case EVENT_TEXT_REMOVED: + if (accessible) { + events.emit(accessible, "text-change"); + } + break; + case EVENT_DOCUMENT_ATTRIBUTES_CHANGED: + case EVENT_OBJECT_ATTRIBUTE_CHANGED: + case EVENT_TEXT_ATTRIBUTE_CHANGED: + if (accessible) { + events.emit(accessible, "attributes-change", accessible.getAttributes()); + } + break; + case EVENT_ACCELERATOR_CHANGE: + if (accessible) { + events.emit(accessible, "shortcut-change", rawAccessible.keyboardShortcut); + } + break; + default: + break; + } + } +}); + +/** + * The AccessibilityActor is a top level container actor that initializes + * accessible walker and is the top-most point of interaction for accessibility + * tools UI. + */ +const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, { + initialize(conn, tabActor) { + Actor.prototype.initialize.call(this, conn); + this.tabActor = tabActor; + }, + + getWalker() { + if (!this.walker) { + this.walker = new AccessibleWalkerActor(this.conn, this.tabActor); + } + return this.walker; + }, + + destroy() { + Actor.prototype.destroy.call(this); + this.walker.destroy(); + this.walker = null; + this.tabActor = null; + } +}); + +exports.AccessibleActor = AccessibleActor; +exports.AccessibleWalkerActor = AccessibleWalkerActor; +exports.AccessibilityActor = AccessibilityActor; diff --git a/devtools/server/actors/moz.build b/devtools/server/actors/moz.build index 5e0e704a9048..1da8a9c94215 100644 --- a/devtools/server/actors/moz.build +++ b/devtools/server/actors/moz.build @@ -11,6 +11,7 @@ DIRS += [ ] DevToolsModules( + 'accessibility.js', 'actor-registry.js', 'addon.js', 'addons.js', diff --git a/devtools/server/main.js b/devtools/server/main.js index ee3ac2cfa7cd..a1fef4b0d5f6 100644 --- a/devtools/server/main.js +++ b/devtools/server/main.js @@ -585,6 +585,11 @@ var DebuggerServer = { constructor: "WebExtensionInspectedWindowActor", type: { tab: true } }); + this.registerModule("devtools/server/actors/accessibility", { + prefix: "accessibility", + constructor: "AccessibilityActor", + type: { tab: true } + }); }, /** diff --git a/devtools/server/tests/browser/browser.ini b/devtools/server/tests/browser/browser.ini index 34c72407c883..93530b3ae743 100644 --- a/devtools/server/tests/browser/browser.ini +++ b/devtools/server/tests/browser/browser.ini @@ -4,6 +4,7 @@ subsuite = devtools support-files = head.js animation.html + doc_accessibility.html doc_allocations.html doc_force_cc.html doc_force_gc.html @@ -25,6 +26,10 @@ support-files = storage-helpers.js !/devtools/server/tests/mochitest/hello-actor.js +[browser_accessibility_node_events.js] +[browser_accessibility_node.js] +[browser_accessibility_simple.js] +[browser_accessibility_walker.js] [browser_animation_emitMutations.js] [browser_animation_getFrames.js] [browser_animation_getProperties.js] diff --git a/devtools/server/tests/browser/browser_accessibility_node.js b/devtools/server/tests/browser/browser_accessibility_node.js new file mode 100644 index 000000000000..6dd16da6bf56 --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_node.js @@ -0,0 +1,74 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Checks for the AccessibleActor + +add_task(function* () { + let {client, walker, accessibility} = + yield initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html"); + + let a11yWalker = yield accessibility.getWalker(walker); + let buttonNode = yield walker.querySelector(walker.rootNode, "#button"); + let accessibleFront = yield a11yWalker.getAccessibleFor(buttonNode); + + checkA11yFront(accessibleFront, { + name: "Accessible Button", + role: "pushbutton", + value: "", + description: "Accessibility Test", + help: "", + keyboardShortcut: "", + childCount: 1, + domNodeType: 1 + }); + + info("Actions"); + let actions = yield accessibleFront.getActions(); + is(actions.length, 1, "Accessible Front has correct number of actions"); + is(actions[0], "Press", "Accessible Front default action is correct"); + + info("Index in parent"); + let index = yield accessibleFront.getIndexInParent(); + is(index, 1, "Accessible Front has correct index in parent"); + + info("State"); + let state = yield accessibleFront.getState(); + SimpleTest.isDeeply(state, + ["focusable", "selectable text", "opaque", "enabled", "sensitive"], + "Accessible Front has correct states"); + + info("Attributes"); + let attributes = yield accessibleFront.getAttributes(); + SimpleTest.isDeeply(attributes, { + "margin-top": "0px", + display: "inline-block", + "text-align": "center", + "text-indent": "0px", + "margin-left": "0px", + tag: "button", + "margin-right": "0px", + id: "button", + "margin-bottom": "0px" + }, "Accessible Front has correct attributes"); + + info("Children"); + let children = yield accessibleFront.children(); + is(children.length, 1, "Accessible Front has correct number of children"); + checkA11yFront(children[0], { + name: "Accessible Button", + role: "text leaf" + }); + + info("DOM Node"); + let node = yield accessibleFront.getDOMNode(walker); + is(node, buttonNode, "Accessible Front has correct DOM node"); + + let a11yShutdown = waitForA11yShutdown(); + yield client.close(); + forceCollections(); + yield a11yShutdown; + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/browser_accessibility_node_events.js b/devtools/server/tests/browser/browser_accessibility_node_events.js new file mode 100644 index 000000000000..2ae6167e286d --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_node_events.js @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Checks for the AccessibleActor events + +add_task(function* () { + let {client, walker, accessibility} = + yield initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html"); + + let a11yWalker = yield accessibility.getWalker(walker); + let a11yDoc = yield a11yWalker.getDocument(); + let buttonNode = yield walker.querySelector(walker.rootNode, "#button"); + let accessibleFront = yield a11yWalker.getAccessibleFor(buttonNode); + let sliderNode = yield walker.querySelector(walker.rootNode, "#slider"); + let accessibleSliderFront = yield a11yWalker.getAccessibleFor(sliderNode); + let browser = gBrowser.selectedBrowser; + + checkA11yFront(accessibleFront, { + name: "Accessible Button", + role: "pushbutton", + value: "", + description: "Accessibility Test", + help: "", + keyboardShortcut: "", + childCount: 1, + domNodeType: 1 + }); + + info("Name change event"); + yield emitA11yEvent(accessibleFront, "name-change", + (name, parent) => { + checkA11yFront(accessibleFront, { name: "Renamed" }); + checkA11yFront(parent, { }, a11yDoc); + }, () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute( + "aria-label", "Renamed"))); + + info("Description change event"); + yield emitA11yEvent(accessibleFront, "description-change", + () => checkA11yFront(accessibleFront, { description: "" }), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").removeAttribute("aria-describedby"))); + + info("State change event"); + let states = yield accessibleFront.getState(); + let expectedStates = ["unavailable", "selectable text", "opaque"]; + SimpleTest.isDeeply(states, ["focusable", "selectable text", "opaque", + "enabled", "sensitive"], "States are correct"); + yield emitA11yEvent(accessibleFront, "state-change", + newStates => SimpleTest.isDeeply(newStates, expectedStates, + "States are updated"), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute("disabled", true))); + states = yield accessibleFront.getState(); + SimpleTest.isDeeply(states, expectedStates, "States are updated"); + + info("Attributes change event"); + let attrs = yield accessibleFront.getAttributes(); + ok(!attrs.live, "Attribute is not present"); + yield emitA11yEvent(accessibleFront, "attributes-change", + newAttrs => is(newAttrs.live, "polite", "Attributes are updated"), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute("aria-live", "polite"))); + attrs = yield accessibleFront.getAttributes(); + is(attrs.live, "polite", "Attributes are updated"); + + info("Value change event"); + checkA11yFront(accessibleSliderFront, { value: "5" }); + yield emitA11yEvent(accessibleSliderFront, "value-change", + () => checkA11yFront(accessibleSliderFront, { value: "6" }), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("slider").setAttribute("aria-valuenow", "6"))); + + info("Reorder event"); + is(accessibleSliderFront.childCount, 1, "Slider has only 1 child"); + yield emitA11yEvent(accessibleSliderFront, "reorder", + childCount => is(childCount, 2, "Child count is updated"), + () => ContentTask.spawn(browser, null, () => { + let button = content.document.createElement("button"); + button.innerText = "Slider button"; + content.document.getElementById("slider").appendChild(button); + })); + is(accessibleSliderFront.childCount, 2, "Child count is updated"); + + let a11yShutdown = waitForA11yShutdown(); + yield client.close(); + forceCollections(); + yield a11yShutdown; + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/browser_accessibility_simple.js b/devtools/server/tests/browser/browser_accessibility_simple.js new file mode 100644 index 000000000000..ef33b095c5ec --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_simple.js @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Simple checks for the AccessibilityActor and AccessibleWalkerActor + +add_task(function* () { + let {client, accessibility} = yield initAccessibilityFrontForUrl( + "data:text/html;charset=utf-8,test
"); + + ok(accessibility, "The AccessibilityFront was created"); + ok(accessibility.getWalker, "The getWalker method exists"); + + let a11yWalker = yield accessibility.getWalker(); + ok(a11yWalker, "The AccessibleWalkerFront was returned"); + + yield client.close(); + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/browser_accessibility_walker.js b/devtools/server/tests/browser/browser_accessibility_walker.js new file mode 100644 index 000000000000..0cafa19747e3 --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_walker.js @@ -0,0 +1,75 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Checks for the AccessibleWalkerActor + +add_task(function* () { + let {client, walker, accessibility} = + yield initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html"); + + let a11yWalker = yield accessibility.getWalker(walker); + ok(a11yWalker, "The AccessibleWalkerFront was returned"); + + let a11yDoc = yield a11yWalker.getDocument(); + ok(a11yDoc, "The AccessibleFront for root doc is created"); + + let children = yield a11yWalker.children(); + is(children.length, 1, + "AccessibleWalker only has 1 child - root doc accessible"); + is(a11yDoc, children[0], + "Root accessible must be AccessibleWalker's only child"); + + let buttonNode = yield walker.querySelector(walker.rootNode, "#button"); + let accessibleFront = yield a11yWalker.getAccessibleFor(buttonNode); + + checkA11yFront(accessibleFront, { + name: "Accessible Button", + role: "pushbutton" + }); + + let browser = gBrowser.selectedBrowser; + + // Ensure name-change event is emitted by walker when cached accessible's name + // gets updated (via DOM manipularion). + yield emitA11yEvent(a11yWalker, "name-change", + (front, parent) => { + checkA11yFront(front, { name: "Renamed" }, accessibleFront); + checkA11yFront(parent, { }, a11yDoc); + }, + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute( + "aria-label", "Renamed"))); + + // Ensure reorder event is emitted by walker when DOM tree changes. + let docChildren = yield a11yDoc.children(); + is(docChildren.length, 3, "Root doc should have correct number of children"); + + yield emitA11yEvent(a11yWalker, "reorder", + front => checkA11yFront(front, { }, a11yDoc), + () => ContentTask.spawn(browser, null, () => { + let input = content.document.createElement("input"); + input.type = "text"; + input.title = "This is a tooltip"; + input.value = "New input"; + content.document.body.appendChild(input); + })); + + docChildren = yield a11yDoc.children(); + is(docChildren.length, 4, "Root doc should have correct number of children"); + + // Ensure destory event is emitted by walker when cached accessible's raw + // accessible gets destroyed. + yield emitA11yEvent(a11yWalker, "accessible-destroy", + destroyedFront => checkA11yFront(destroyedFront, { }, accessibleFront), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").remove())); + + let a11yShutdown = waitForA11yShutdown(); + yield client.close(); + forceCollections(); + yield a11yShutdown; + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/doc_accessibility.html b/devtools/server/tests/browser/doc_accessibility.html new file mode 100644 index 000000000000..ca44efdc2f65 --- /dev/null +++ b/devtools/server/tests/browser/doc_accessibility.html @@ -0,0 +1,12 @@ + + + + + + +

Accessibility Test

+ +
slider
+ + diff --git a/devtools/server/tests/browser/head.js b/devtools/server/tests/browser/head.js index fc2bb47f477d..03b0791bac31 100644 --- a/devtools/server/tests/browser/head.js +++ b/devtools/server/tests/browser/head.js @@ -80,6 +80,22 @@ function* initLayoutFrontForUrl(url) { return {inspector, walker, layout, client}; } +function* initAccessibilityFrontForUrl(url) { + const {AccessibilityFront} = require("devtools/shared/fronts/accessibility"); + const {InspectorFront} = require("devtools/shared/fronts/inspector"); + + yield addTab(url); + + initDebuggerServer(); + let client = new DebuggerClient(DebuggerServer.connectPipe()); + let form = yield connectDebuggerClient(client); + let inspector = InspectorFront(client, form); + let walker = yield inspector.getWalker(); + let accessibility = AccessibilityFront(client, form); + + return {inspector, walker, accessibility, client}; +} + function initDebuggerServer() { try { // Sometimes debugger server does not get destroyed correctly by previous @@ -236,3 +252,53 @@ function waitForMarkerType(front, types, predicate, function getCookieId(name, domain, path) { return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`; } + +/** + * Trigger DOM activity and wait for the corresponding accessibility event. + * @param {Object} emitter Devtools event emitter, usually a front. + * @param {Sting} name Accessibility event in question. + * @param {Function} handler Accessibility event handler function with checks. + * @param {Promise} task A promise that resolves when DOM activity is done. + */ +async function emitA11yEvent(emitter, name, handler, task) { + let promise = emitter.once(name, handler); + await task(); + await promise; +} + +/** + * Check that accessibilty front is correct and its attributes are also + * up-to-date. + * @param {Object} front Accessibility front to be tested. + * @param {Object} expected A map of a11y front properties to be verified. + * @param {Object} expectedFront Expected accessibility front. + */ +function checkA11yFront(front, expected, expectedFront) { + ok(front, "The accessibility front is created"); + + if (expectedFront) { + is(front, expectedFront, "Matching accessibility front"); + } + + for (let key in expected) { + is(front[key], expected[key], `accessibility front has correct ${key}`); + } +} + +/** + * Wait for accessibility service to shut down. We consider it shut down when + * an "a11y-init-or-shutdown" event is received with a value of "0". + */ +async function waitForA11yShutdown() { + await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => + new Promise(resolve => { + let observe = (subject, topic, data) => { + Services.obs.removeObserver(observe, "a11y-init-or-shutdown"); + + if (data === "0") { + resolve(); + } + }; + Services.obs.addObserver(observe, "a11y-init-or-shutdown"); + })); +} diff --git a/devtools/shared/fronts/accessibility.js b/devtools/shared/fronts/accessibility.js new file mode 100644 index 000000000000..8497ea11af6c --- /dev/null +++ b/devtools/shared/fronts/accessibility.js @@ -0,0 +1,136 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const { + Front, + FrontClassWithSpec, + preEvent, + types +} = require("devtools/shared/protocol.js"); +const { + accessibleSpec, + accessibleWalkerSpec, + accessibilitySpec +} = require("devtools/shared/specs/accessibility"); + +const events = require("devtools/shared/event-emitter"); +const ACCESSIBLE_PROPERTIES = [ + "role", + "name", + "value", + "description", + "help", + "keyboardShortcut", + "childCount", + "domNodeType" +]; + +const AccessibleFront = FrontClassWithSpec(accessibleSpec, { + initialize(client, form) { + Front.prototype.initialize.call(this, client, form); + + // Define getters for accesible properties that are received from the actor. + // Note: we would like accessible properties to be iterable for a11y + // clients. + for (let key of ACCESSIBLE_PROPERTIES) { + Object.defineProperty(this, key, { + get() { + return this._form[key]; + }, + enumerable: true + }); + } + }, + + marshallPool() { + return this.walker; + }, + + form(form, detail) { + if (detail === "actorid") { + this.actorID = form; + return; + } + + this.actorID = form.actor; + this._form = form; + DevToolsUtils.defineLazyGetter(this, "walker", () => + types.getType("accessiblewalker").read(this._form.walker, this)); + }, + + /** + * Get a dom node front from accessible actor's raw accessible object's + * DONNode property. + */ + getDOMNode(domWalker) { + return domWalker.getNodeFromActor(this.actorID, + ["rawAccessible", "DOMNode"]); + }, + + nameChange: preEvent("name-change", function (name, parent) { + this._form.name = name; + // Name change event affects the tree rendering, we fire this event on + // accessibility walker as the point of interaction for UI. + if (this.walker) { + events.emit(this.walker, "name-change", this, parent); + } + }), + + valueChange: preEvent("value-change", function (value) { + this._form.value = value; + }), + + descriptionChange: preEvent("description-change", function (description) { + this._form.description = description; + }), + + helpChange: preEvent("help-change", function (help) { + this._form.help = help; + }), + + shortcutChange: preEvent("shortcut-change", function (keyboardShortcut) { + this._form.keyboardShortcut = keyboardShortcut; + }), + + reorder: preEvent("reorder", function (childCount) { + this._form.childCount = childCount; + // Reorder event affects the tree rendering, we fire this event on + // accessibility walker as the point of interaction for UI. + if (this.walker) { + events.emit(this.walker, "reorder", this); + } + }), + + textChange: preEvent("text-change", function () { + // Text event affects the tree rendering, we fire this event on + // accessibility walker as the point of interaction for UI. + if (this.walker) { + events.emit(this.walker, "text-change", this); + } + }) +}); + +const AccessibleWalkerFront = FrontClassWithSpec(accessibleWalkerSpec, { + accessibleDestroy: preEvent("accessible-destroy", function (accessible) { + accessible.destroy(); + }), + + form(json) { + this.actorID = json.actor; + } +}); + +const AccessibilityFront = FrontClassWithSpec(accessibilitySpec, { + initialize(client, form) { + Front.prototype.initialize.call(this, client, form); + this.actorID = form.accessibilityActor; + this.manage(this); + } +}); + +exports.AccessibleFront = AccessibleFront; +exports.AccessibleWalkerFront = AccessibleWalkerFront; +exports.AccessibilityFront = AccessibilityFront; diff --git a/devtools/shared/fronts/moz.build b/devtools/shared/fronts/moz.build index d8d823db9026..fac715128d8e 100644 --- a/devtools/shared/fronts/moz.build +++ b/devtools/shared/fronts/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'accessibility.js', 'actor-registry.js', 'addons.js', 'animation.js', diff --git a/devtools/shared/specs/accessibility.js b/devtools/shared/specs/accessibility.js new file mode 100644 index 000000000000..f44c156f03f1 --- /dev/null +++ b/devtools/shared/specs/accessibility.js @@ -0,0 +1,141 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const protocol = require("devtools/shared/protocol"); +const { Arg, generateActorSpec, RetVal, types } = protocol; +// eslint-disable-next-line no-unused-vars +const { nodeSpec } = require("devtools/shared/specs/inspector"); + +types.addActorType("accessible"); + +const accessibleSpec = generateActorSpec({ + typeName: "accessible", + + events: { + "actions-change": { + type: "actionsChange", + actions: Arg(0, "array:string") + }, + "name-change": { + type: "nameChange", + name: Arg(0, "string"), + parent: Arg(1, "nullable:accessible") + }, + "value-change": { + type: "valueChange", + value: Arg(0, "string") + }, + "description-change": { + type: "descriptionChange", + description: Arg(0, "string") + }, + "state-change": { + type: "stateChange", + states: Arg(0, "array:string") + }, + "attributes-change": { + type: "attributesChange", + states: Arg(0, "json") + }, + "help-change": { + type: "helpChange", + help: Arg(0, "string") + }, + "shortcut-change": { + type: "shortcutChange", + shortcut: Arg(0, "string") + }, + "reorder": { + type: "reorder", + childCount: Arg(0, "number") + }, + "text-change": { + type: "textChange" + } + }, + + methods: { + getActions: { + request: {}, + response: { + actions: RetVal("array:string") + } + }, + getIndexInParent: { + request: {}, + response: { + indexInParent: RetVal("number") + } + }, + getState: { + request: {}, + response: { + states: RetVal("array:string") + } + }, + getAttributes: { + request: {}, + response: { + attributes: RetVal("json") + } + }, + children: { + request: {}, + response: { + children: RetVal("array:accessible") + } + } + } +}); + +const accessibleWalkerSpec = generateActorSpec({ + typeName: "accessiblewalker", + + events: { + "accessible-destroy": { + type: "accessibleDestroy", + accessible: Arg(0, "accessible") + } + }, + + methods: { + children: { + request: {}, + response: { + children: RetVal("array:accessible") + } + }, + getDocument: { + request: {}, + response: { + document: RetVal("accessible") + } + }, + getAccessibleFor: { + request: { node: Arg(0, "domnode") }, + response: { + accessible: RetVal("accessible") + } + } + } +}); + +const accessibilitySpec = generateActorSpec({ + typeName: "accessibility", + + methods: { + getWalker: { + request: {}, + response: { + walker: RetVal("accessiblewalker") + } + } + } +}); + +exports.accessibleSpec = accessibleSpec; +exports.accessibleWalkerSpec = accessibleWalkerSpec; +exports.accessibilitySpec = accessibilitySpec; diff --git a/devtools/shared/specs/moz.build b/devtools/shared/specs/moz.build index 4262f4123953..ffb8f1ab65f8 100644 --- a/devtools/shared/specs/moz.build +++ b/devtools/shared/specs/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'accessibility.js', 'actor-registry.js', 'addons.js', 'animation.js', diff --git a/dom/base/SubtleCrypto.cpp b/dom/base/SubtleCrypto.cpp index 410ead0554c4..56dfa8bd0bbb 100644 --- a/dom/base/SubtleCrypto.cpp +++ b/dom/base/SubtleCrypto.cpp @@ -58,7 +58,7 @@ SubtleCrypto::RecordTelemetryOnce() { RefPtr task = \ WebCryptoTask::Create ## Operation ## Task(__VA_ARGS__); \ if (!task) { \ - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); \ + aRv.Throw(NS_ERROR_NULL_POINTER); \ return nullptr; \ } \ task->DispatchWithPromise(p); \ diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index e666a55020cd..b5d29ce41f24 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -305,6 +305,7 @@ bool nsContentUtils::sGetBoxQuadsEnabled = false; bool nsContentUtils::sSkipCursorMoveForSameValueSet = false; bool nsContentUtils::sRequestIdleCallbackEnabled = false; bool nsContentUtils::sLowerNetworkPriority = false; +bool nsContentUtils::sTailingEnabled = false; bool nsContentUtils::sShowInputPlaceholderOnFocus = true; bool nsContentUtils::sAutoFocusEnabled = true; #ifndef RELEASE_OR_BETA @@ -769,6 +770,9 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sLowerNetworkPriority, "privacy.trackingprotection.lower_network_priority", false); + Preferences::AddBoolVarCache(&sTailingEnabled, + "network.http.tailing.enabled", true); + Preferences::AddBoolVarCache(&sShowInputPlaceholderOnFocus, "dom.placeholder.show_on_focus", true); @@ -3085,22 +3089,15 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent, /* aDeep = */ true, /* aLiveList = */ false); } - RefPtr htmlFormControls = htmlDoc->GetExistingFormControls(); - if (!htmlFormControls) { - // If the document doesn't have an existing form controls content list, - // create a new one, but avoid creating a live list since we only need to - // use the list here and it doesn't need to listen to mutation events. - htmlFormControls = new nsContentList(aDocument, - nsHTMLDocument::MatchFormControls, - nullptr, nullptr, - /* aDeep = */ true, - /* aMatchAtom = */ nullptr, - /* aMatchNameSpaceId = */ kNameSpaceID_None, - /* aFuncMayDependOnAttr = */ true, - /* aLiveList = */ false); - } - - NS_ENSURE_TRUE(htmlForms && htmlFormControls, NS_ERROR_OUT_OF_MEMORY); + RefPtr htmlFormControls = + new nsContentList(aDocument, + nsHTMLDocument::MatchFormControls, + nullptr, nullptr, + /* aDeep = */ true, + /* aMatchAtom = */ nullptr, + /* aMatchNameSpaceId = */ kNameSpaceID_None, + /* aFuncMayDependOnAttr = */ true, + /* aLiveList = */ false); // If we have a form control and can calculate form information, use that // as the key - it is more reliable than just recording position in the @@ -3117,7 +3114,7 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent, // XXXbz We don't? Why not? I don't follow. // nsCOMPtr control(do_QueryInterface(aContent)); - if (control && htmlFormControls && htmlForms) { + if (control) { // Append the control type KeyAppendInt(control->ControlType(), aKey); diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 7c6c334b84d3..7aa904ea2910 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -3110,6 +3110,9 @@ public: // if we want to lower the priority of the channel. static bool IsLowerNetworkPriority() { return sLowerNetworkPriority; } + // Whether tracker tailing is turned on - "network.http.tailing.enabled". + static bool IsTailingEnabled() { return sTailingEnabled; } + // Check pref "dom.placeholder.show_on_focus" to see // if we want to show the placeholder inside input elements // when they have focus. @@ -3301,6 +3304,7 @@ private: static bool sSkipCursorMoveForSameValueSet; static bool sRequestIdleCallbackEnabled; static bool sLowerNetworkPriority; + static bool sTailingEnabled; static bool sShowInputPlaceholderOnFocus; static bool sAutoFocusEnabled; #ifndef RELEASE_OR_BETA diff --git a/dom/base/test/browser_bug1058164.js b/dom/base/test/browser_bug1058164.js index 54f2740b42b3..472b4ac3f1ce 100644 --- a/dom/base/test/browser_bug1058164.js +++ b/dom/base/test/browser_bug1058164.js @@ -69,7 +69,7 @@ add_task(async function test_swap_frameloader_pagevisibility_events() { // We have to wait for the window to load so we can get the selected browser // to listen to. - await BrowserTestUtils.waitForEvent(newWindow, "load"); + await BrowserTestUtils.waitForEvent(newWindow, "DOMContentLoaded"); let newWindowBrowser = newWindow.gBrowser.selectedBrowser; // Wait for the expected pagehide and pageshow events on the initial browser diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index b00c98e4934e..33e03b89618f 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -14957,9 +14957,15 @@ class CGBindingImplClass(CGClass): if m.isMethod(): if m.isIdentifierLess(): continue + if m.isMaplikeOrSetlikeOrIterableMethod(): + # Handled by generated code already + continue if not m.isStatic() or not skipStaticMethods: appendMethod(m) elif m.isAttr(): + if m.isMaplikeOrSetlikeAttr(): + # Handled by generated code already + continue self.methodDecls.append(cgGetter(descriptor, m)) if not m.readonly: self.methodDecls.append(cgSetter(descriptor, m)) @@ -15213,8 +15219,9 @@ class CGExampleRoot(CGThing): continue if member.isStatic(): builder.addInMozillaDom("GlobalObject") - if member.isAttr() and not member.isMaplikeOrSetlikeAttr(): - builder.forwardDeclareForType(member.type, config) + if member.isAttr(): + if not member.isMaplikeOrSetlikeAttr(): + builder.forwardDeclareForType(member.type, config) else: assert member.isMethod() if not member.isMaplikeOrSetlikeOrIterableMethod(): @@ -15563,6 +15570,17 @@ class CGJSImplClass(CGBindingImplClass): static=True, body=self.getCreateFromExistingBody())) + if (descriptor.interface.isJSImplemented() and + descriptor.interface.maplikeOrSetlikeOrIterable and + descriptor.interface.maplikeOrSetlikeOrIterable.isMaplike()): + self.methodDecls.append( + ClassMethod("__OnGet", + "void", + [Argument("JS::Handle", "aKey"), + Argument("JS::Handle", "aValue"), + Argument("ErrorResult&", "aRv")], + body="mImpl->__OnGet(aKey, aValue, aRv);\n")) + CGClass.__init__(self, descriptor.name, bases=baseClasses, constructors=[constructor], @@ -15909,25 +15927,46 @@ class CGFastCallback(CGClass): class CGCallbackInterface(CGCallback): def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False): iface = descriptor.interface - attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] + attrs = [m for m in iface.members + if (m.isAttr() and not m.isStatic() and + (not m.isMaplikeOrSetlikeAttr() or + not iface.isJSImplemented()))] getters = [CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs) for a in attrs] setters = [CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs) for a in attrs if not a.readonly] methods = [m for m in iface.members - if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] + if (m.isMethod() and not m.isStatic() and + not m.isIdentifierLess() and + (not m.isMaplikeOrSetlikeOrIterableMethod() or + not iface.isJSImplemented()))] methods = [CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs) for m in methods for sig in m.signatures()] + + needInitId = False if iface.isJSImplemented() and iface.ctor(): sigs = descriptor.interface.ctor().signatures() if len(sigs) != 1: raise TypeError("We only handle one constructor. See bug 869268.") methods.append(CGJSImplInitOperation(sigs[0], descriptor)) - if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()): - methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name) - for m in iface.members - if m.isAttr() or m.isMethod()] + - (["__init"] if iface.isJSImplemented() and iface.ctor() else []), + needInitId = True + + needOnGetId = False + if (iface.isJSImplemented() and + iface.maplikeOrSetlikeOrIterable and + iface.maplikeOrSetlikeOrIterable.isMaplike()): + methods.append(CGJSImplOnGetOperation(descriptor)) + needOnGetId = True + + idlist = [descriptor.binaryNameFor(m.identifier.name) + for m in iface.members + if m.isAttr() or m.isMethod()] + if needInitId: + idlist.append("__init") + if needOnGetId: + idlist.append("__onget") + if len(idlist) != 0: + methods.append(initIdsClassMethod(idlist, iface.identifier.name + "Atoms")) CGCallback.__init__(self, iface, descriptor, "CallbackInterface", methods, getters=getters, setters=setters) @@ -16446,6 +16485,32 @@ class CGJSImplInitOperation(CallbackOperationBase): return "__init" +class CGJSImplOnGetOperation(CallbackOperationBase): + """ + Codegen the __OnGet() method used to notify the JS impl that a get() is + happening on a JS-implemented maplike. This method takes two arguments + (key and value) and returns nothing. + """ + def __init__(self, descriptor): + CallbackOperationBase.__init__( + self, + (BuiltinTypes[IDLBuiltinType.Types.void], + [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.any], + None, + "key"), + FakeArgument(BuiltinTypes[IDLBuiltinType.Types.any], + None, + "value")]), + "__onget", "__OnGet", + descriptor, + singleOperation=False, + rethrowContentException=True, + spiderMonkeyInterfacesAreStructs=True) + + def getPrettyName(self): + return "__onget" + + def getMaplikeOrSetlikeErrorReturn(helperImpl): """ Generate return values based on whether a maplike or setlike generated @@ -16728,7 +16793,21 @@ class CGMaplikeOrSetlikeMethodGenerator(CGThing): JS::Rooted result(cx); """))] arguments = ["&result"] - return self.mergeTuples(r, (code, arguments, [])) + if self.descriptor.interface.isJSImplemented(): + callOnGet = [CGGeneric(dedent( + """ + { + JS::ExposeValueToActiveJS(result); + ErrorResult onGetResult; + self->__OnGet(arg0Val, result, onGetResult); + if (onGetResult.MaybeSetPendingException(cx)) { + return false; + } + } + """))] + else: + callOnGet = [] + return self.mergeTuples(r, (code, arguments, callOnGet)) def has(self): """ @@ -17028,6 +17107,11 @@ class GlobalGenRoots(): if d.interface.isJSImplemented() and d.interface.ctor(): # We'll have an __init() method. members.append(FakeMember('__init')) + if (d.interface.isJSImplemented() and + d.interface.maplikeOrSetlikeOrIterable and + d.interface.maplikeOrSetlikeOrIterable.isMaplike()): + # We'll have an __onget() method. + members.append(FakeMember('__onget')) if len(members) == 0: continue diff --git a/dom/bindings/test/TestInterfaceJSMaplike.js b/dom/bindings/test/TestInterfaceJSMaplike.js index b108ef5b6123..d6d5ef7e8904 100644 --- a/dom/bindings/test/TestInterfaceJSMaplike.js +++ b/dom/bindings/test/TestInterfaceJSMaplike.js @@ -32,7 +32,11 @@ TestInterfaceJSMaplike.prototype = { clearInternal: function() { return this.__DOM_IMPL__.__clear(); - } + }, + + __onget: function(key, value) { + /* no-op */ + }, }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJSMaplike]) diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 2a5f2a14e982..ad420a9e13da 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -393,10 +393,12 @@ FetchDriver::HttpFetch() } } - if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) { + if (mIsTrackingFetch && nsContentUtils::IsTailingEnabled()) { cos->AddClassFlags(nsIClassOfService::Throttleable | nsIClassOfService::Tail); + } + if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) { nsCOMPtr p = do_QueryInterface(chan); if (p) { p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST); diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index 310e4a455331..2ba4076a621b 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -203,7 +203,6 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsHTMLDocument, nsDocument, mAnchors, mScripts, mForms, - mFormControls, mWyciwygChannel, mMidasCommandManager) @@ -3719,7 +3718,6 @@ nsHTMLDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const // - mAnchors // - mScripts // - mForms - // - mFormControls // - mWyciwygChannel // - mMidasCommandManager } diff --git a/dom/html/nsHTMLDocument.h b/dom/html/nsHTMLDocument.h index fc0da755d5a5..65609f5c3f1d 100644 --- a/dom/html/nsHTMLDocument.h +++ b/dom/html/nsHTMLDocument.h @@ -82,11 +82,6 @@ public: return mForms; } - nsContentList* GetExistingFormControls() const - { - return mFormControls; - } - // nsIDOMDocument interface using nsDocument::CreateElement; using nsDocument::CreateElementNS; @@ -324,7 +319,6 @@ protected: RefPtr mAnchors; RefPtr mScripts; RefPtr mForms; - RefPtr mFormControls; RefPtr mAll; diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index 338de7849d89..4ab7b718ca58 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -327,6 +327,10 @@ class RTCStatsReport { } get mozPcid() { return this._pcid; } + + __onget(key, value) { + /* Do whatever here */ + } } setupPrototype(RTCStatsReport, { classID: PC_STATS_CID, diff --git a/dom/media/webrtc/RTCCertificate.cpp b/dom/media/webrtc/RTCCertificate.cpp index 20eb0abf8003..628e46adeb9f 100644 --- a/dom/media/webrtc/RTCCertificate.cpp +++ b/dom/media/webrtc/RTCCertificate.cpp @@ -280,10 +280,6 @@ RTCCertificate::GenerateCertificate( RefPtr task = new GenerateRTCCertificateTask(global, aGlobal.Context(), aOptions, usages, expires); - if (!task) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - } task->DispatchWithPromise(p); return p.forget(); } diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index ad79c71d6c6f..982503a81094 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -40,7 +40,6 @@ #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/IntegerRange.h" #include "mozilla/Mutex.h" -#include "mozilla/LazyIdleThread.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" @@ -3593,11 +3592,10 @@ QuotaManager::Init(const nsAString& aBasePath) return rv; } - // Make a lazy thread for any IO we need (like clearing or enumerating the - // contents of storage directories). - mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, - NS_LITERAL_CSTRING("Storage I/O"), - LazyIdleThread::ManualShutdown); + rv = NS_NewNamedThread("QuotaManager IO", getter_AddRefs(mIOThread)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } // Make a timer here to avoid potential failures later. We don't actually // initialize the timer until shutdown. diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index de654faa6451..ff5823e78df9 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -1067,7 +1067,12 @@ ScriptLoader::StartLoad(ScriptLoadRequest* aRequest) // synchronous head scripts block loading of most other non js/css // content such as images, Leader implicitely disallows tailing cos->AddClassFlags(nsIClassOfService::Leader); - } else if (defer && !async) { + } else if (defer && (!async || !nsContentUtils::IsTailingEnabled())) { + // Bug 1395525 and the !nsContentUtils::IsTailingEnabled() bit: + // We want to make sure that turing tailing off by the pref makes + // the browser behave exactly the same way as before landing + // the tailing patch, which has added the "&& !async" part. + // head/body deferred scripts are blocked by leaders but are not // allowed tailing because they block DOMContentLoaded cos->AddClassFlags(nsIClassOfService::TailForbidden); diff --git a/dom/system/nsDeviceSensors.cpp b/dom/system/nsDeviceSensors.cpp index 3faf6185e73f..59050f73f632 100644 --- a/dom/system/nsDeviceSensors.cpp +++ b/dom/system/nsDeviceSensors.cpp @@ -283,41 +283,53 @@ struct Orientation }; static Orientation -RotationVectorToOrientation(double aX, double aY, double aZ, double aW) -{ - static const double kFuzzyOne = 1.0 - 1e-6; - static const double kCircleRad = 2.0 * M_PI; +RotationVectorToOrientation(double aX, double aY, double aZ, double aW) { + double mat[9]; - Orientation orient = { 2.0 * std::atan2(aY, aW), - M_PI_2, - 0.0 }; + mat[0] = 1 - 2*aY*aY - 2*aZ*aZ; + mat[1] = 2*aX*aY - 2*aZ*aW; + mat[2] = 2*aX*aZ + 2*aY*aW; - const double sqX = aX * aX; - const double sqY = aY * aY; - const double sqZ = aZ * aZ; - const double sqW = aW * aW; - const double unitLength = sqX + sqY + sqZ + sqW; - const double xwyz = 2.0 * (aX * aW + aY * aZ) / unitLength; + mat[3] = 2*aX*aY + 2*aZ*aW; + mat[4] = 1 - 2*aX*aX - 2*aZ*aZ; + mat[5] = 2*aY*aZ - 2*aX*aW; - if (xwyz < -kFuzzyOne) { - orient.alpha *= -1.0; - orient.beta *= -1.0; - } else if (xwyz <= kFuzzyOne) { - const double gammaX = -sqX - sqY + sqZ + sqW; - const double gammaY = 2.0 * (aY * aW - aX * aZ); - const double alphaX = -sqX + sqY - sqZ + sqW; - const double alphaY = 2.0 * (aZ * aW - aX * aY); - const double fac = gammaX > 0 ? 1.0 : -1.0; + mat[6] = 2*aX*aZ - 2*aY*aW; + mat[7] = 2*aY*aZ + 2*aX*aW; + mat[8] = 1 - 2*aX*aX - 2*aY*aY; - orient.alpha = std::fmod(kCircleRad + std::atan2(fac * alphaY, fac * alphaX), - kCircleRad); - orient.beta = fac * std::asin(xwyz); - orient.gamma = std::atan2(fac * gammaY, fac * gammaX); - if (fac < 0.0) { - orient.beta = fmod(M_PI + orient.beta, M_PI); + Orientation orient; + + if (mat[8] > 0) { + orient.alpha = atan2(-mat[1], mat[4]); + orient.beta = asin(mat[7]); + orient.gamma = atan2(-mat[6], mat[8]); + } else if (mat[8] < 0) { + orient.alpha = atan2(mat[1], -mat[4]); + orient.beta = -asin(mat[7]); + orient.beta += (orient.beta >= 0) ? -M_PI : M_PI; + orient.gamma = atan2(mat[6], -mat[8]); + } else { + if (mat[6] > 0) { + orient.alpha = atan2(-mat[1], mat[4]); + orient.beta = asin(mat[7]); + orient.gamma = -M_PI_2; + } else if (mat[6] < 0) { + orient.alpha = atan2(mat[1], -mat[4]); + orient.beta = -asin(mat[7]); + orient.beta += (orient.beta >= 0) ? -M_PI : M_PI; + orient.gamma = -M_PI_2; + } else { + orient.alpha = atan2(mat[3], mat[0]); + orient.beta = (mat[7] > 0) ? M_PI_2 : -M_PI_2; + orient.gamma = 0; } } + if (orient.alpha < 0) { + orient.alpha += 2*M_PI; + } + return Orientation::RadToDeg(orient); } diff --git a/dom/webauthn/libudev-sys/Cargo.toml b/dom/webauthn/libudev-sys/Cargo.toml new file mode 100644 index 000000000000..78cf6c51214d --- /dev/null +++ b/dom/webauthn/libudev-sys/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "libudev-sys" +version = "0.1.3" +authors = ["Tim Taubert "] +description = "FFI bindings to libudev" + +[dependencies] +lazy_static = "0.2" +libc = "0.2" diff --git a/dom/webauthn/libudev-sys/src/lib.rs b/dom/webauthn/libudev-sys/src/lib.rs new file mode 100644 index 000000000000..c422399d8521 --- /dev/null +++ b/dom/webauthn/libudev-sys/src/lib.rs @@ -0,0 +1,180 @@ +/* -*- Mode: rust; rust-indent-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/. */ + +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] + +#[macro_use] +extern crate lazy_static; +extern crate libc; + +use libc::{c_void,c_int,c_char,c_ulonglong,dev_t}; +use libc::{RTLD_GLOBAL,RTLD_LAZY,RTLD_NOLOAD}; +use libc::{dlopen,dlclose,dlsym}; +use std::ffi::CString; +use std::{marker,mem,ops,ptr}; + +#[repr(C)] +pub struct udev { + __private: c_void +} + +#[repr(C)] +pub struct udev_list_entry { + __private: c_void +} + +#[repr(C)] +pub struct udev_device { + __private: c_void +} + +#[repr(C)] +pub struct udev_monitor { + __private: c_void +} + +#[repr(C)] +pub struct udev_enumerate { + __private: c_void +} + +macro_rules! ifnull { + ($a:expr, $b:expr) => { + if $a.is_null() { $b } else { $a } + } +} + +struct Library(*mut c_void); + +impl Library { + fn open(name: &'static str) -> Library { + let flags = RTLD_LAZY | RTLD_GLOBAL; + let flags_noload = flags | RTLD_NOLOAD; + let name = CString::new(name).unwrap(); + let name = name.as_ptr(); + + Library(unsafe { + ifnull!(dlopen(name, flags_noload), dlopen(name, flags)) + }) + } + + fn get(&self, name: &'static str) -> *mut c_void { + let name = CString::new(name).unwrap(); + unsafe { dlsym(self.0, name.as_ptr()) } + } +} + +impl Drop for Library { + fn drop(&mut self) { + unsafe { dlclose(self.0); } + } +} + +unsafe impl Sync for Library {} + +lazy_static! { + static ref LIBRARY: Library = { + Library::open("libudev.so.1") + }; +} + +pub struct Symbol { + ptr: *mut c_void, + pd: marker::PhantomData +} + +impl Symbol { + fn new(ptr: *mut c_void) -> Self { + let default = Self::default as *mut c_void; + Self { ptr: ifnull!(ptr, default), pd: marker::PhantomData } + } + + // This is the default symbol, used whenever dlopen() fails. + // Users of this library are expected to check whether udev_new() returns + // a nullptr, and if so they MUST NOT call any other exported functions. + extern "C" fn default() -> *mut c_void { + ptr::null_mut() + } +} + +impl ops::Deref for Symbol { + type Target = T; + + fn deref(&self) -> &T { + unsafe { mem::transmute(&self.ptr) } + } +} + +unsafe impl Sync for Symbol {} + +macro_rules! define { + ($name:ident, $type:ty) => { + lazy_static! { + pub static ref $name : Symbol<$type> = { + Symbol::new(LIBRARY.get(stringify!($name))) + }; + } + }; +} + +// udev +define!(udev_new, extern "C" fn () -> *mut udev); +define!(udev_unref, extern "C" fn (*mut udev) -> *mut udev); + +// udev_list +define!(udev_list_entry_get_next, extern "C" fn (*mut udev_list_entry) -> *mut udev_list_entry); +define!(udev_list_entry_get_name, extern "C" fn (*mut udev_list_entry) -> *const c_char); +define!(udev_list_entry_get_value, extern "C" fn (*mut udev_list_entry) -> *const c_char); + +// udev_device +define!(udev_device_ref, extern "C" fn (*mut udev_device) -> *mut udev_device); +define!(udev_device_unref, extern "C" fn (*mut udev_device) -> *mut udev_device); +define!(udev_device_new_from_syspath, extern "C" fn (*mut udev, *const c_char) -> *mut udev_device); +define!(udev_device_get_parent, extern "C" fn (*mut udev_device) -> *mut udev_device); +define!(udev_device_get_devpath, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_subsystem, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_devtype, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_syspath, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_sysname, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_sysnum, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_devnode, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_is_initialized, extern "C" fn (*mut udev_device) -> c_int); +define!(udev_device_get_properties_list_entry, extern "C" fn (*mut udev_device) -> *mut udev_list_entry); +define!(udev_device_get_property_value, extern "C" fn (*mut udev_device, *const c_char) -> *const c_char); +define!(udev_device_get_driver, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_devnum, extern "C" fn (*mut udev_device) -> dev_t); +define!(udev_device_get_action, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_sysattr_value, extern "C" fn (*mut udev_device, *const c_char) -> *const c_char); +define!(udev_device_set_sysattr_value, extern "C" fn (*mut udev_device, *const c_char, *mut c_char) -> c_int); +define!(udev_device_get_sysattr_list_entry, extern "C" fn (*mut udev_device) -> *mut udev_list_entry); +define!(udev_device_get_seqnum, extern "C" fn (*mut udev_device) -> c_ulonglong); + +// udev_monitor +define!(udev_monitor_ref, extern "C" fn (*mut udev_monitor) -> *mut udev_monitor); +define!(udev_monitor_unref, extern "C" fn (*mut udev_monitor) -> *mut udev_monitor); +define!(udev_monitor_new_from_netlink, extern "C" fn (*mut udev, *const c_char) -> *mut udev_monitor); +define!(udev_monitor_enable_receiving, extern "C" fn (*mut udev_monitor) -> c_int); +define!(udev_monitor_get_fd, extern "C" fn (*mut udev_monitor) -> c_int); +define!(udev_monitor_receive_device, extern "C" fn (*mut udev_monitor) -> *mut udev_device); +define!(udev_monitor_filter_add_match_subsystem_devtype, extern "C" fn (*mut udev_monitor, *const c_char, *const c_char) -> c_int); +define!(udev_monitor_filter_add_match_tag, extern "C" fn (*mut udev_monitor, *const c_char) -> c_int); +define!(udev_monitor_filter_remove, extern "C" fn (*mut udev_monitor) -> c_int); + +// udev_enumerate +define!(udev_enumerate_unref, extern "C" fn (*mut udev_enumerate) -> *mut udev_enumerate); +define!(udev_enumerate_new, extern "C" fn (*mut udev) -> *mut udev_enumerate); +define!(udev_enumerate_add_match_subsystem, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_nomatch_subsystem, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_match_sysattr, extern "C" fn (*mut udev_enumerate, *const c_char, *const c_char) -> c_int); +define!(udev_enumerate_add_nomatch_sysattr, extern "C" fn (*mut udev_enumerate, *const c_char, *const c_char) -> c_int); +define!(udev_enumerate_add_match_property, extern "C" fn (*mut udev_enumerate, *const c_char, *const c_char) -> c_int); +define!(udev_enumerate_add_match_tag, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_match_parent, extern "C" fn (*mut udev_enumerate, *mut udev_device) -> c_int); +define!(udev_enumerate_add_match_is_initialized, extern "C" fn (*mut udev_enumerate) -> c_int); +define!(udev_enumerate_add_match_sysname, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_syspath, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_scan_devices, extern "C" fn (*mut udev_enumerate) -> c_int); +define!(udev_enumerate_get_list_entry, extern "C" fn (*mut udev_enumerate) -> *mut udev_list_entry); diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 4591fee2079a..e9048785644e 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -2621,13 +2621,15 @@ XMLHttpRequestMainThread::MaybeLowerChannelPriority() return; } - nsCOMPtr cos = do_QueryInterface(mChannel); - if (cos) { - // Adding TailAllowed to overrule the Unblocked flag, but to preserve - // the effect of Unblocked when tailing is off. - cos->AddClassFlags(nsIClassOfService::Throttleable | - nsIClassOfService::Tail | - nsIClassOfService::TailAllowed); + if (nsContentUtils::IsTailingEnabled()) { + nsCOMPtr cos = do_QueryInterface(mChannel); + if (cos) { + // Adding TailAllowed to overrule the Unblocked flag, but to preserve + // the effect of Unblocked when tailing is off. + cos->AddClassFlags(nsIClassOfService::Throttleable | + nsIClassOfService::Tail | + nsIClassOfService::TailAllowed); + } } nsCOMPtr p = do_QueryInterface(mChannel); diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index 5358830a9d7b..dc8687456ac4 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -74,6 +74,7 @@ class Mutex; namespace gfx { class UnscaledFont; +class ScaledFont; } template<> @@ -89,6 +90,15 @@ struct WeakPtrTraits } }; +template<> +struct WeakPtrTraits +{ + static void AssertSafeToAccessFromNonOwningThread() + { + AssertIsMainThreadOrServoFontMetricsLocked(); + } +}; + namespace gfx { class ScaledFont; @@ -769,23 +779,29 @@ protected: UnscaledFont() {} private: - static uint32_t sDeletionCounter; + static Atomic sDeletionCounter; }; /** This class is an abstraction of a backend/platform specific font object * at a particular size. It is passed into text drawing calls to describe * the font used for the drawing call. */ -class ScaledFont : public external::AtomicRefCounted +class ScaledFont + : public external::AtomicRefCounted + , public SupportsWeakPtr { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont) - virtual ~ScaledFont() {} + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ScaledFont) + + virtual ~ScaledFont(); virtual FontType GetType() const = 0; virtual Float GetSize() const = 0; virtual AntialiasMode GetDefaultAAMode(); + static uint32_t DeletionCounter() { return sDeletionCounter; } + /** This allows getting a path that describes the outline of a set of glyphs. * A target is passed in so that the guarantee is made the returned path * can be used with any DrawTarget that has the same backend as the one @@ -833,6 +849,9 @@ protected: UserData mUserData; RefPtr mUnscaledFont; + +private: + static Atomic sDeletionCounter; }; /** diff --git a/gfx/2d/ScaledFontBase.cpp b/gfx/2d/ScaledFontBase.cpp index 5fb7bcf56443..298e58dce60f 100644 --- a/gfx/2d/ScaledFontBase.cpp +++ b/gfx/2d/ScaledFontBase.cpp @@ -26,13 +26,20 @@ using namespace std; namespace mozilla { namespace gfx { -uint32_t UnscaledFont::sDeletionCounter = 0; +Atomic UnscaledFont::sDeletionCounter(0); UnscaledFont::~UnscaledFont() { sDeletionCounter++; } +Atomic ScaledFont::sDeletionCounter(0); + +ScaledFont::~ScaledFont() +{ + sDeletionCounter++; +} + AntialiasMode ScaledFont::GetDefaultAAMode() { diff --git a/gfx/doc/README.webrender b/gfx/doc/README.webrender index 5a397acc6c33..bda0068902bb 100644 --- a/gfx/doc/README.webrender +++ b/gfx/doc/README.webrender @@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc the need to run the cargo update command in js/src as well. Hopefully this will be resolved soon. -Latest Commit: 5edd3da7ee11e1d0caaf0b53cb7f04cfab20e585 +Latest Commit: 81cba6b139c4c1061cab6a1c38acf2ae7f50445d diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index 7cb6421b4235..fb966479a1ce 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -315,6 +315,11 @@ public: nsIntRegion& aOutRegion, NotifySubDocInvalidationFunc aCallback) { + if (mLayer->AsHostLayer() && !mLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) { + IntRect result = NewTransformedBoundsForLeaf(); + result = result.Union(OldTransformedBoundsForLeaf()); + aOutRegion = result; + } return true; } diff --git a/gfx/layers/ipc/PWebRenderBridge.ipdl b/gfx/layers/ipc/PWebRenderBridge.ipdl index 069447fdff03..fad50b7acca2 100644 --- a/gfx/layers/ipc/PWebRenderBridge.ipdl +++ b/gfx/layers/ipc/PWebRenderBridge.ipdl @@ -22,6 +22,9 @@ using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::FontInstanceKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeFontInstanceOptions from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeFontInstancePlatformOptions from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h"; using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h"; @@ -54,6 +57,9 @@ parent: async DeleteCompositorAnimations(uint64_t[] aIds); async AddRawFont(FontKey aFontKey, ByteBuffer aBytes, uint32_t aFontIndex); async DeleteFont(FontKey aFontKey); + async AddFontInstance(FontInstanceKey aInstanceKey, FontKey aFontKey, float aGlyphSize, + MaybeFontInstanceOptions aOptions, MaybeFontInstancePlatformOptions aPlatformOptions); + async DeleteFontInstance(FontInstanceKey aInstanceKey); async DPBegin(IntSize aSize); async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId, LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc, diff --git a/gfx/layers/wr/WebRenderBridgeChild.cpp b/gfx/layers/wr/WebRenderBridgeChild.cpp index 4ba078141ba1..6a7dd8dbc84a 100644 --- a/gfx/layers/wr/WebRenderBridgeChild.cpp +++ b/gfx/layers/wr/WebRenderBridgeChild.cpp @@ -29,6 +29,7 @@ WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId) , mIPCOpen(false) , mDestroyed(false) , mFontKeysDeleted(0) + , mFontInstanceKeysDeleted(0) { } @@ -219,7 +220,7 @@ WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArra MOZ_ASSERT(aFont); MOZ_ASSERT(!aGlyphs.IsEmpty()); - wr::WrFontKey key = GetFontKeyForScaledFont(aFont); + wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont); MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle); nsTArray wr_glyph_instances; @@ -235,11 +236,10 @@ WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArra aSc.ToRelativeLayoutRect(aClip), aColor, key, - Range(wr_glyph_instances.Elements(), wr_glyph_instances.Length()), - aFont->GetSize()); + Range(wr_glyph_instances.Elements(), wr_glyph_instances.Length())); } -wr::FontKey +wr::FontInstanceKey WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont) { MOZ_ASSERT(!mDestroyed); @@ -248,34 +248,54 @@ WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont) (aScaledFont->GetType() == gfx::FontType::MAC) || (aScaledFont->GetType() == gfx::FontType::FONTCONFIG)); + wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 }; + if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) { + return instanceKey; + } + RefPtr unscaled = aScaledFont->GetUnscaledFont(); MOZ_ASSERT(unscaled); - wr::FontKey key = { wr::IdNamespace { 0 }, 0}; - if (mFontKeys.Get(unscaled, &key)) { - return key; + wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0}; + if (!mFontKeys.Get(unscaled, &fontKey)) { + FontFileData data; + if (!unscaled->GetFontFileData(WriteFontFileData, &data) || + !data.mFontBuffer.mData) { + return instanceKey; + } + + fontKey.mNamespace = GetNamespace(); + fontKey.mHandle = GetNextResourceId(); + + SendAddRawFont(fontKey, data.mFontBuffer, data.mFontIndex); + + mFontKeys.Put(unscaled, fontKey); } - FontFileData data; - if (!unscaled->GetFontFileData(WriteFontFileData, &data) || - !data.mFontBuffer.mData) { - return key; - } + instanceKey.mNamespace = GetNamespace(); + instanceKey.mHandle = GetNextResourceId(); - key.mNamespace = GetNamespace(); - key.mHandle = GetNextResourceId(); + SendAddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(), Nothing(), Nothing()); - SendAddRawFont(key, data.mFontBuffer, data.mFontIndex); + mFontInstanceKeys.Put(aScaledFont, instanceKey); - mFontKeys.Put(unscaled, key); - - return key; + return instanceKey; } void WebRenderBridgeChild::RemoveExpiredFontKeys() { - uint32_t counter = gfx::UnscaledFont::DeletionCounter(); + uint32_t counter = gfx::ScaledFont::DeletionCounter(); + if (mFontInstanceKeysDeleted != counter) { + mFontInstanceKeysDeleted = counter; + for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) { + if (!iter.Key()) { + SendDeleteFontInstance(iter.Data()); + iter.Remove(); + } + } + } + counter = gfx::UnscaledFont::DeletionCounter(); if (mFontKeysDeleted != counter) { mFontKeysDeleted = counter; for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) { @@ -459,7 +479,8 @@ WebRenderBridgeChild::RecvWrUpdated(const wr::IdNamespace& aNewIdNamespace) // Update mIdNamespace to identify obsolete keys and messages by WebRenderBridgeParent. // Since usage of invalid keys could cause crash in webrender. mIdNamespace = aNewIdNamespace; - // Just clear FontKeys, they are removed during WebRenderAPI destruction. + // Just clear FontInstaceKeys/FontKeys, they are removed during WebRenderAPI destruction. + mFontInstanceKeys.Clear(); mFontKeys.Clear(); GetCompositorBridgeChild()->RecvInvalidateLayers(wr::AsUint64(mPipelineId)); return IPC_OK(); diff --git a/gfx/layers/wr/WebRenderBridgeChild.h b/gfx/layers/wr/WebRenderBridgeChild.h index 026b703c7916..9f9376f97d08 100644 --- a/gfx/layers/wr/WebRenderBridgeChild.h +++ b/gfx/layers/wr/WebRenderBridgeChild.h @@ -27,13 +27,14 @@ class CompositorBridgeChild; class StackingContextHelper; class TextureForwarder; -class UnscaledFontHashKey : public PLDHashEntryHdr +template +class WeakPtrHashKey : public PLDHashEntryHdr { public: - typedef gfx::UnscaledFont* KeyType; - typedef const gfx::UnscaledFont* KeyTypePointer; + typedef T* KeyType; + typedef const T* KeyTypePointer; - explicit UnscaledFontHashKey(KeyTypePointer aKey) : mKey(const_cast(aKey)) {} + explicit WeakPtrHashKey(KeyTypePointer aKey) : mKey(const_cast(aKey)) {} KeyType GetKey() const { return mKey; } bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } @@ -46,9 +47,12 @@ public: enum { ALLOW_MEMMOVE = true }; private: - WeakPtr mKey; + WeakPtr mKey; }; +typedef WeakPtrHashKey UnscaledFontHashKey; +typedef WeakPtrHashKey ScaledFontHashKey; + class WebRenderBridgeChild final : public PWebRenderBridgeChild , public CompositableForwarder { @@ -109,7 +113,7 @@ public: const StackingContextHelper& aSc, const LayerRect& aBounds, const LayerRect& aClip); - wr::FontKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont); + wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont); void RemoveExpiredFontKeys(); void ClearReadLocks(); @@ -179,6 +183,9 @@ private: uint32_t mFontKeysDeleted; nsDataHashtable mFontKeys; + + uint32_t mFontInstanceKeysDeleted; + nsDataHashtable mFontInstanceKeys; }; } // namespace layers diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 0cc062d822bf..2a3b9c3cbf7f 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -312,8 +312,7 @@ WebRenderBridgeParent::RecvDeleteFont(const wr::FontKey& aFontKey) return IPC_OK(); } - if (mFontKeys.find(wr::AsUint64(aFontKey)) != mFontKeys.end()) { - mFontKeys.erase(wr::AsUint64(aFontKey)); + if (mFontKeys.erase(wr::AsUint64(aFontKey)) > 0) { mApi->DeleteFont(aFontKey); } else { MOZ_ASSERT_UNREACHABLE("invalid FontKey"); @@ -322,6 +321,54 @@ WebRenderBridgeParent::RecvDeleteFont(const wr::FontKey& aFontKey) return IPC_OK(); } +mozilla::ipc::IPCResult +WebRenderBridgeParent::RecvAddFontInstance(const wr::FontInstanceKey& aInstanceKey, + const wr::FontKey& aFontKey, + const float& aGlyphSize, + const MaybeFontInstanceOptions& aOptions, + const MaybeFontInstancePlatformOptions& aPlatformOptions) +{ + if (mDestroyed) { + return IPC_OK(); + } + + // Check if key is obsoleted. + if (aInstanceKey.mNamespace != mIdNamespace) { + return IPC_OK(); + } + + MOZ_ASSERT(mApi); + MOZ_ASSERT(mFontInstanceKeys.find(wr::AsUint64(aInstanceKey)) == mFontInstanceKeys.end()); + + mFontInstanceKeys.insert(wr::AsUint64(aInstanceKey)); + mApi->AddFontInstance(aInstanceKey, aFontKey, aGlyphSize, + aOptions.ptrOr(nullptr), aPlatformOptions.ptrOr(nullptr)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +WebRenderBridgeParent::RecvDeleteFontInstance(const wr::FontInstanceKey& aInstanceKey) +{ + if (mDestroyed) { + return IPC_OK(); + } + MOZ_ASSERT(mApi); + + // Check if key is obsoleted. + if (aInstanceKey.mNamespace != mIdNamespace) { + return IPC_OK(); + } + + if (mFontInstanceKeys.erase(wr::AsUint64(aInstanceKey)) > 0) { + mApi->DeleteFontInstance(aInstanceKey); + } else { + MOZ_ASSERT_UNREACHABLE("invalid FontInstanceKey"); + } + + return IPC_OK(); +} + mozilla::ipc::IPCResult WebRenderBridgeParent::RecvUpdateImage(const wr::ImageKey& aImageKey, const gfx::IntSize& aSize, @@ -357,8 +404,7 @@ WebRenderBridgeParent::RecvDeleteImage(const wr::ImageKey& aImageKey) return IPC_OK(); } - if (mActiveImageKeys.find(wr::AsUint64(aImageKey)) != mActiveImageKeys.end()) { - mActiveImageKeys.erase(wr::AsUint64(aImageKey)); + if (mActiveImageKeys.erase(wr::AsUint64(aImageKey)) > 0) { mKeysToDelete.push_back(aImageKey); } else { MOZ_ASSERT_UNREACHABLE("invalid ImageKey"); @@ -374,9 +420,8 @@ WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray } for (uint32_t i = 0; i < aIds.Length(); i++) { - if (mActiveAnimations.find(aIds[i]) != mActiveAnimations.end()) { + if (mActiveAnimations.erase(aIds[i]) > 0) { mAnimStorage->ClearById(aIds[i]); - mActiveAnimations.erase(aIds[i]); } else { NS_ERROR("Tried to delete invalid animation"); } @@ -1307,6 +1352,7 @@ WebRenderBridgeParent::ClearResources() // Schedule composition to clean up Pipeline mCompositorScheduler->ScheduleComposition(); // WrFontKeys and WrImageKeys are deleted during WebRenderAPI destruction. + mFontInstanceKeys.clear(); mFontKeys.clear(); mActiveImageKeys.clear(); mKeysToDelete.clear(); diff --git a/gfx/layers/wr/WebRenderBridgeParent.h b/gfx/layers/wr/WebRenderBridgeParent.h index d3a6eac81839..ca3e5fba3354 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.h +++ b/gfx/layers/wr/WebRenderBridgeParent.h @@ -92,6 +92,12 @@ public: const ByteBuffer& aBuffer, const uint32_t& aFontIndex) override; mozilla::ipc::IPCResult RecvDeleteFont(const wr::FontKey& aFontKey) override; + mozilla::ipc::IPCResult RecvAddFontInstance(const wr::FontInstanceKey& aInstanceKey, + const wr::FontKey& aFontKey, + const float& aGlyphSize, + const MaybeFontInstanceOptions& aOptions, + const MaybeFontInstancePlatformOptions& aPlatformOptions) override; + mozilla::ipc::IPCResult RecvDeleteFontInstance(const wr::FontInstanceKey& aInstanceKey) override; mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize) override; mozilla::ipc::IPCResult RecvDPEnd(const gfx::IntSize& aSize, InfallibleTArray&& aCommands, @@ -272,6 +278,7 @@ private: // WebRenderBridgeParent is destroyed abnormally and Tab move between different windows. std::unordered_set mActiveImageKeys; std::unordered_set mFontKeys; + std::unordered_set mFontInstanceKeys; // mActiveAnimations is used to avoid leaking animations when WebRenderBridgeParent is // destroyed abnormally and Tab move between different windows. std::unordered_set mActiveAnimations; diff --git a/gfx/layers/wr/WebRenderMessageUtils.h b/gfx/layers/wr/WebRenderMessageUtils.h index 7bbc7fdafe93..56694eb72b51 100644 --- a/gfx/layers/wr/WebRenderMessageUtils.h +++ b/gfx/layers/wr/WebRenderMessageUtils.h @@ -53,6 +53,24 @@ struct ParamTraits { }; +template<> +struct ParamTraits + : public PlainOldDataSerializer +{ +}; + +template<> +struct ParamTraits + : public PlainOldDataSerializer +{ +}; + +template<> +struct ParamTraits + : public PlainOldDataSerializer +{ +}; + template<> struct ParamTraits : public PlainOldDataSerializer diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml index 9d545c503e9f..3bcb481990b4 100644 --- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender" -version = "0.49.0" +version = "0.50.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" diff --git a/gfx/webrender/examples/basic.rs b/gfx/webrender/examples/basic.rs index 9f1e99fa3e41..e19b62021b13 100644 --- a/gfx/webrender/examples/basic.rs +++ b/gfx/webrender/examples/basic.rs @@ -241,6 +241,9 @@ impl Example for App { let font_bytes = load_file("res/FreeSans.ttf"); resources.add_raw_font(font_key, font_bytes, 0); + let font_instance_key = api.generate_font_instance_key(); + resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None); + let text_bounds = (100, 200).by(700, 300); let glyphs = vec![ GlyphInstance { @@ -296,9 +299,8 @@ impl Example for App { builder.push_text(text_bounds, None, &glyphs, - font_key, + font_instance_key, ColorF::new(1.0, 1.0, 0.0, 1.0), - Au::from_px(32), None); } diff --git a/gfx/webrender/examples/blob.rs b/gfx/webrender/examples/blob.rs index 159e40dacdf0..2e0da7c9c9f2 100644 --- a/gfx/webrender/examples/blob.rs +++ b/gfx/webrender/examples/blob.rs @@ -210,6 +210,7 @@ impl api::BlobImageRenderer for CheckerboardRenderer { Err(api::BlobImageError::Other("Channel closed".into())) } fn delete_font(&mut self, _font: api::FontKey) { } + fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) { } } struct App { diff --git a/gfx/webrender/res/cs_box_shadow.glsl b/gfx/webrender/res/cs_box_shadow.glsl index 987e2e638150..ee458dce48a6 100644 --- a/gfx/webrender/res/cs_box_shadow.glsl +++ b/gfx/webrender/res/cs_box_shadow.glsl @@ -183,6 +183,6 @@ void main(void) { float value = color(pos, p0Rect, p1Rect, radii, sigma); value = max(value, 0.0); - oFragColor = dither(vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value)); + oFragColor = dither(vec4(vInverted == 1.0 ? 1.0 - value : value)); } #endif diff --git a/gfx/webrender/res/cs_clip_rectangle.fs.glsl b/gfx/webrender/res/cs_clip_rectangle.fs.glsl deleted file mode 100644 index 3539587b994e..000000000000 --- a/gfx/webrender/res/cs_clip_rectangle.fs.glsl +++ /dev/null @@ -1,65 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -float clip_against_ellipse_if_needed(vec2 pos, - float current_distance, - vec4 ellipse_center_radius, - vec2 sign_modifier, - float afwidth) { - float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy, - ellipse_center_radius.zw); - - return mix(current_distance, - ellipse_distance + afwidth, - all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))); -} - -float rounded_rect(vec2 pos) { - float current_distance = 0.0; - - // Apply AA - float afwidth = 0.5 * length(fwidth(pos)); - - // Clip against each ellipse. - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_TL, - vec2(1.0), - afwidth); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_TR, - vec2(-1.0, 1.0), - afwidth); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_BR, - vec2(-1.0), - afwidth); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_BL, - vec2(1.0, -1.0), - afwidth); - - return smoothstep(0.0, afwidth, 1.0 - current_distance); -} - - -void main(void) { - float alpha = 1.f; - vec2 local_pos = init_transform_fs(vPos, alpha); - - float clip_alpha = rounded_rect(local_pos); - - float combined_alpha = min(alpha, clip_alpha); - - // Select alpha or inverse alpha depending on clip in/out. - float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode); - - oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0); -} diff --git a/gfx/webrender/res/cs_clip_rectangle.glsl b/gfx/webrender/res/cs_clip_rectangle.glsl index 581bf5e2ba01..00392e2aab94 100644 --- a/gfx/webrender/res/cs_clip_rectangle.glsl +++ b/gfx/webrender/res/cs_clip_rectangle.glsl @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include shared,prim_shared,clip_shared +#include shared,prim_shared,clip_shared,ellipse varying vec3 vPos; flat varying float vClipMode; @@ -10,3 +10,142 @@ flat varying vec4 vClipCenter_Radius_TL; flat varying vec4 vClipCenter_Radius_TR; flat varying vec4 vClipCenter_Radius_BL; flat varying vec4 vClipCenter_Radius_BR; + +#ifdef WR_VERTEX_SHADER +struct ClipRect { + RectWithSize rect; + vec4 mode; +}; + +ClipRect fetch_clip_rect(ivec2 address) { + vec4 data[2] = fetch_from_resource_cache_2_direct(address); + return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]); +} + +struct ClipCorner { + RectWithSize rect; + vec4 outer_inner_radius; +}; + +ClipCorner fetch_clip_corner(ivec2 address, int index) { + address += ivec2(2 + 2 * index, 0); + vec4 data[2] = fetch_from_resource_cache_2_direct(address); + return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]); +} + +struct ClipData { + ClipRect rect; + ClipCorner top_left; + ClipCorner top_right; + ClipCorner bottom_left; + ClipCorner bottom_right; +}; + +ClipData fetch_clip(ivec2 address) { + ClipData clip; + + clip.rect = fetch_clip_rect(address); + clip.top_left = fetch_clip_corner(address, 0); + clip.top_right = fetch_clip_corner(address, 1); + clip.bottom_left = fetch_clip_corner(address, 2); + clip.bottom_right = fetch_clip_corner(address, 3); + + return clip; +} + +void main(void) { + CacheClipInstance cci = fetch_clip_item(gl_InstanceID); + ClipArea area = fetch_clip_area(cci.render_task_index); + Layer layer = fetch_layer(cci.layer_index); + ClipData clip = fetch_clip(cci.clip_data_address); + RectWithSize local_rect = clip.rect.rect; + + ClipVertexInfo vi = write_clip_tile_vertex(local_rect, + layer, + area, + cci.segment); + vPos = vi.local_pos; + + vClipMode = clip.rect.mode.x; + + RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect); + + vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy, + clip.top_left.outer_inner_radius.xy); + + vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x, + clip_rect.p0.y + clip.top_right.outer_inner_radius.y, + clip.top_right.outer_inner_radius.xy); + + vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy, + clip.bottom_right.outer_inner_radius.xy); + + vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x, + clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y, + clip.bottom_left.outer_inner_radius.xy); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +float clip_against_ellipse_if_needed(vec2 pos, + float current_distance, + vec4 ellipse_center_radius, + vec2 sign_modifier, + float afwidth) { + float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy, + ellipse_center_radius.zw); + + return mix(current_distance, + ellipse_distance + afwidth, + all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))); +} + +float rounded_rect(vec2 pos) { + float current_distance = 0.0; + + // Apply AA + float afwidth = 0.5 * length(fwidth(pos)); + + // Clip against each ellipse. + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_TL, + vec2(1.0), + afwidth); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_TR, + vec2(-1.0, 1.0), + afwidth); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_BR, + vec2(-1.0), + afwidth); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_BL, + vec2(1.0, -1.0), + afwidth); + + return smoothstep(0.0, afwidth, 1.0 - current_distance); +} + + +void main(void) { + float alpha = 1.f; + vec2 local_pos = init_transform_fs(vPos, alpha); + + float clip_alpha = rounded_rect(local_pos); + + float combined_alpha = min(alpha, clip_alpha); + + // Select alpha or inverse alpha depending on clip in/out. + float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode); + + oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0); +} +#endif diff --git a/gfx/webrender/res/cs_clip_rectangle.vs.glsl b/gfx/webrender/res/cs_clip_rectangle.vs.glsl deleted file mode 100644 index 6a4282ed4abc..000000000000 --- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl +++ /dev/null @@ -1,76 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -struct ClipRect { - RectWithSize rect; - vec4 mode; -}; - -ClipRect fetch_clip_rect(ivec2 address) { - vec4 data[2] = fetch_from_resource_cache_2_direct(address); - return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]); -} - -struct ClipCorner { - RectWithSize rect; - vec4 outer_inner_radius; -}; - -ClipCorner fetch_clip_corner(ivec2 address, int index) { - address += ivec2(2 + 2 * index, 0); - vec4 data[2] = fetch_from_resource_cache_2_direct(address); - return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]); -} - -struct ClipData { - ClipRect rect; - ClipCorner top_left; - ClipCorner top_right; - ClipCorner bottom_left; - ClipCorner bottom_right; -}; - -ClipData fetch_clip(ivec2 address) { - ClipData clip; - - clip.rect = fetch_clip_rect(address); - clip.top_left = fetch_clip_corner(address, 0); - clip.top_right = fetch_clip_corner(address, 1); - clip.bottom_left = fetch_clip_corner(address, 2); - clip.bottom_right = fetch_clip_corner(address, 3); - - return clip; -} - -void main(void) { - CacheClipInstance cci = fetch_clip_item(gl_InstanceID); - ClipArea area = fetch_clip_area(cci.render_task_index); - Layer layer = fetch_layer(cci.layer_index); - ClipData clip = fetch_clip(cci.clip_data_address); - RectWithSize local_rect = clip.rect.rect; - - ClipVertexInfo vi = write_clip_tile_vertex(local_rect, - layer, - area, - cci.segment); - vPos = vi.local_pos; - - vClipMode = clip.rect.mode.x; - - RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect); - - vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy, - clip.top_left.outer_inner_radius.xy); - - vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x, - clip_rect.p0.y + clip.top_right.outer_inner_radius.y, - clip.top_right.outer_inner_radius.xy); - - vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy, - clip.bottom_right.outer_inner_radius.xy); - - vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x, - clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y, - clip.bottom_left.outer_inner_radius.xy); -} diff --git a/gfx/webrender/res/ellipse.glsl b/gfx/webrender/res/ellipse.glsl new file mode 100644 index 000000000000..5d07669402c0 --- /dev/null +++ b/gfx/webrender/res/ellipse.glsl @@ -0,0 +1,69 @@ +/* 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/. */ + +#ifdef WR_FRAGMENT_SHADER + +// +// Signed distance to an ellipse. +// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm +// Note that this fails for exact circles. +// +float sdEllipse( vec2 p, in vec2 ab ) { + p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; } + float l = ab.y*ab.y - ab.x*ab.x; + + float m = ab.x*p.x/l; + float n = ab.y*p.y/l; + float m2 = m*m; + float n2 = n*n; + + float c = (m2 + n2 - 1.0)/3.0; + float c3 = c*c*c; + + float q = c3 + m2*n2*2.0; + float d = c3 + m2*n2; + float g = m + m*n2; + + float co; + + if( d<0.0 ) + { + float p = acos(q/c3)/3.0; + float s = cos(p); + float t = sin(p)*sqrt(3.0); + float rx = sqrt( -c*(s + t + 2.0) + m2 ); + float ry = sqrt( -c*(s - t + 2.0) + m2 ); + co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0; + } + else + { + float h = 2.0*m*n*sqrt( d ); + float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 ); + float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 ); + float rx = -s - u - c*4.0 + 2.0*m2; + float ry = (s - u)*sqrt(3.0); + float rm = sqrt( rx*rx + ry*ry ); + float p = ry/sqrt(rm-rx); + co = (p + 2.0*g/rm - m)/2.0; + } + + float si = sqrt( 1.0 - co*co ); + + vec2 r = vec2( ab.x*co, ab.y*si ); + + return length(r - p ) * sign(p.y-r.y); +} + +float distance_to_ellipse(vec2 p, vec2 radii) { + // sdEllipse fails on exact circles, so handle equal + // radii here. The branch coherency should make this + // a performance win for the circle case too. + if (radii.x == radii.y) { + return length(p) - radii.x; + } else { + return sdEllipse(p, radii); + } +} + +#endif diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index 887241ea2aef..c706130620f9 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -855,66 +855,4 @@ vec4 sample_gradient(int address, float offset, float gradient_repeat) { return dither(mix(texels[0], texels[1], fract(x))); } -// -// Signed distance to an ellipse. -// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm -// Note that this fails for exact circles. -// -float sdEllipse( vec2 p, in vec2 ab ) { - p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; } - float l = ab.y*ab.y - ab.x*ab.x; - - float m = ab.x*p.x/l; - float n = ab.y*p.y/l; - float m2 = m*m; - float n2 = n*n; - - float c = (m2 + n2 - 1.0)/3.0; - float c3 = c*c*c; - - float q = c3 + m2*n2*2.0; - float d = c3 + m2*n2; - float g = m + m*n2; - - float co; - - if( d<0.0 ) - { - float p = acos(q/c3)/3.0; - float s = cos(p); - float t = sin(p)*sqrt(3.0); - float rx = sqrt( -c*(s + t + 2.0) + m2 ); - float ry = sqrt( -c*(s - t + 2.0) + m2 ); - co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0; - } - else - { - float h = 2.0*m*n*sqrt( d ); - float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 ); - float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 ); - float rx = -s - u - c*4.0 + 2.0*m2; - float ry = (s - u)*sqrt(3.0); - float rm = sqrt( rx*rx + ry*ry ); - float p = ry/sqrt(rm-rx); - co = (p + 2.0*g/rm - m)/2.0; - } - - float si = sqrt( 1.0 - co*co ); - - vec2 r = vec2( ab.x*co, ab.y*si ); - - return length(r - p ) * sign(p.y-r.y); -} - -float distance_to_ellipse(vec2 p, vec2 radii) { - // sdEllipse fails on exact circles, so handle equal - // radii here. The branch coherency should make this - // a performance win for the circle case too. - if (radii.x == radii.y) { - return length(p) - radii.x; - } else { - return sdEllipse(p, radii); - } -} - #endif //WR_FRAGMENT_SHADER diff --git a/gfx/webrender/res/ps_border_corner.fs.glsl b/gfx/webrender/res/ps_border_corner.fs.glsl deleted file mode 100644 index 763c2b6daf3e..000000000000 --- a/gfx/webrender/res/ps_border_corner.fs.glsl +++ /dev/null @@ -1,85 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void main(void) { - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - alpha = 0.0; - vec2 local_pos = init_transform_fs(vLocalPos, alpha); -#else - vec2 local_pos = vLocalPos; -#endif - - alpha = min(alpha, do_clip()); - - // Find the appropriate distance to apply the AA smoothstep over. - vec2 fw = fwidth(local_pos); - float afwidth = length(fw); - float distance_for_color; - float color_mix_factor; - - // Only apply the clip AA if inside the clip region. This is - // necessary for correctness when the border width is greater - // than the border radius. - if (all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) { - vec2 p = local_pos - vClipCenter; - - // Get signed distance from the inner/outer clips. - float d0 = distance_to_ellipse(p, vRadii0.xy); - float d1 = distance_to_ellipse(p, vRadii0.zw); - float d2 = distance_to_ellipse(p, vRadii1.xy); - float d3 = distance_to_ellipse(p, vRadii1.zw); - - // SDF subtract main radii - float d_main = max(d0, 0.5 * afwidth - d1); - - // SDF subtract inner radii (double style borders) - float d_inner = max(d2 - 0.5 * afwidth, -d3); - - // Select how to combine the SDF based on border style. - float d = mix(max(d_main, -d_inner), d_main, vSDFSelect); - - // Only apply AA to fragments outside the signed distance field. - alpha = min(alpha, 1.0 - smoothstep(0.0, 0.5 * afwidth, d)); - - // Get the groove/ridge mix factor. - color_mix_factor = smoothstep(-0.5 * afwidth, - 0.5 * afwidth, - -d2); - } else { - // Handle the case where the fragment is outside the clip - // region in a corner. This occurs when border width is - // greater than border radius. - - // Get linear distances along horizontal and vertical edges. - vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz); - vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw); - // Apply union to get the outer edge signed distance. - float da = min(d0.x, d1.x); - // Apply intersection to get the inner edge signed distance. - float db = max(-d0.y, -d1.y); - // Apply union to get both edges. - float d = min(da, db); - // Select fragment on/off based on signed distance. - // No AA here, since we know we're on a straight edge - // and the width is rounded to a whole CSS pixel. - alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); - - // Get the groove/ridge mix factor. - // TODO(gw): Support AA for groove/ridge border edge with transforms. - color_mix_factor = mix(0.0, 1.0, da > 0.0); - } - - // Mix inner/outer color. - vec4 color0 = mix(vColor00, vColor01, color_mix_factor); - vec4 color1 = mix(vColor10, vColor11, color_mix_factor); - - // Select color based on side of line. Get distance from the - // reference line, and then apply AA along the edge. - float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos); - float m = smoothstep(-0.5 * afwidth, 0.5 * afwidth, ld); - vec4 color = mix(color0, color1, m); - - oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); -} diff --git a/gfx/webrender/res/ps_border_corner.glsl b/gfx/webrender/res/ps_border_corner.glsl index 70ba7b04042e..c3ce7f4794d0 100644 --- a/gfx/webrender/res/ps_border_corner.glsl +++ b/gfx/webrender/res/ps_border_corner.glsl @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include shared,prim_shared,shared_border +#include shared,prim_shared,shared_border,ellipse // Edge color transition flat varying vec4 vColor00; @@ -27,3 +27,371 @@ varying vec3 vLocalPos; #else varying vec2 vLocalPos; #endif + +#ifdef WR_VERTEX_SHADER +// Matches BorderCornerSide enum in border.rs +#define SIDE_BOTH 0 +#define SIDE_FIRST 1 +#define SIDE_SECOND 2 + +vec2 get_radii(vec2 radius, vec2 invalid) { + if (all(greaterThan(radius, vec2(0.0)))) { + return radius; + } + + return invalid; +} + +void set_radii(int style, + vec2 radii, + vec2 widths, + vec2 adjusted_widths) { + vRadii0.xy = get_radii(radii, 2.0 * widths); + vRadii0.zw = get_radii(radii - widths, -widths); + + switch (style) { + case BORDER_STYLE_RIDGE: + case BORDER_STYLE_GROOVE: + vRadii1.xy = radii - adjusted_widths; + // See comment in default branch + vRadii1.zw = vec2(-100.0); + break; + case BORDER_STYLE_DOUBLE: + vRadii1.xy = get_radii(radii - adjusted_widths, -widths); + vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths); + break; + default: + // These aren't needed, so we set them to some reasonably large + // negative value so later computations will discard them. This + // avoids branches and numerical issues in the fragment shader. + vRadii1.xy = vec2(-100.0); + vRadii1.zw = vec2(-100.0); + break; + } +} + +void set_edge_line(vec2 border_width, + vec2 outer_corner, + vec2 gradient_sign) { + vec2 gradient = border_width * gradient_sign; + vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x)); +} + +void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) { + vec4 modulate; + + switch (style) { + case BORDER_STYLE_GROOVE: + modulate = vec4(1.0 - 0.3 * delta.x, + 1.0 + 0.3 * delta.x, + 1.0 - 0.3 * delta.y, + 1.0 + 0.3 * delta.y); + + break; + case BORDER_STYLE_RIDGE: + modulate = vec4(1.0 + 0.3 * delta.x, + 1.0 - 0.3 * delta.x, + 1.0 + 0.3 * delta.y, + 1.0 - 0.3 * delta.y); + break; + default: + modulate = vec4(1.0); + break; + } + + // Optionally mask out one side of the border corner, + // depending on the instance kind. + switch (instance_kind) { + case SIDE_FIRST: + color0.a = 0.0; + break; + case SIDE_SECOND: + color1.a = 0.0; + break; + } + + vColor00 = vec4(color0.rgb * modulate.x, color0.a); + vColor01 = vec4(color0.rgb * modulate.y, color0.a); + vColor10 = vec4(color1.rgb * modulate.z, color1.a); + vColor11 = vec4(color1.rgb * modulate.w, color1.a); +} + +int select_style(int color_select, vec2 fstyle) { + ivec2 style = ivec2(fstyle); + + switch (color_select) { + case SIDE_BOTH: + { + // TODO(gw): A temporary hack! While we don't support + // border corners that have dots or dashes + // with another style, pretend they are solid + // border corners. + bool has_dots = style.x == BORDER_STYLE_DOTTED || + style.y == BORDER_STYLE_DOTTED; + bool has_dashes = style.x == BORDER_STYLE_DASHED || + style.y == BORDER_STYLE_DASHED; + if (style.x != style.y && (has_dots || has_dashes)) + return BORDER_STYLE_SOLID; + return style.x; + } + case SIDE_FIRST: + return style.x; + case SIDE_SECOND: + return style.y; + } +} + +void main(void) { + Primitive prim = load_primitive(); + Border border = fetch_border(prim.specific_prim_address); + int sub_part = prim.user_data0; + BorderCorners corners = get_border_corners(border, prim.local_rect); + + vec2 p0, p1; + + // TODO(gw): We'll need to pass through multiple styles + // once we support style transitions per corner. + int style; + vec4 edge_distances; + vec4 color0, color1; + vec2 color_delta; + + // TODO(gw): Now that all border styles are supported, the switch + // statement below can be tidied up quite a bit. + + switch (sub_part) { + case 0: { + p0 = corners.tl_outer; + p1 = corners.tl_inner; + color0 = border.colors[0]; + color1 = border.colors[1]; + vClipCenter = corners.tl_outer + border.radii[0].xy; + vClipSign = vec2(1.0); + style = select_style(prim.user_data1, border.style.yx); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[0].xy, + border.widths.xy, + adjusted_widths.xy); + set_edge_line(border.widths.xy, + corners.tl_outer, + vec2(1.0, 1.0)); + edge_distances = vec4(p0 + adjusted_widths.xy, + p0 + inv_adjusted_widths.xy); + color_delta = vec2(1.0); + break; + } + case 1: { + p0 = vec2(corners.tr_inner.x, corners.tr_outer.y); + p1 = vec2(corners.tr_outer.x, corners.tr_inner.y); + color0 = border.colors[1]; + color1 = border.colors[2]; + vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w); + vClipSign = vec2(-1.0, 1.0); + style = select_style(prim.user_data1, border.style.zy); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[0].zw, + border.widths.zy, + adjusted_widths.zy); + set_edge_line(border.widths.zy, + corners.tr_outer, + vec2(-1.0, 1.0)); + edge_distances = vec4(p1.x - adjusted_widths.z, + p0.y + adjusted_widths.y, + p1.x - border.widths.z + adjusted_widths.z, + p0.y + inv_adjusted_widths.y); + color_delta = vec2(1.0, -1.0); + break; + } + case 2: { + p0 = corners.br_inner; + p1 = corners.br_outer; + color0 = border.colors[2]; + color1 = border.colors[3]; + vClipCenter = corners.br_outer - border.radii[1].xy; + vClipSign = vec2(-1.0, -1.0); + style = select_style(prim.user_data1, border.style.wz); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[1].xy, + border.widths.zw, + adjusted_widths.zw); + set_edge_line(border.widths.zw, + corners.br_outer, + vec2(-1.0, -1.0)); + edge_distances = vec4(p1.x - adjusted_widths.z, + p1.y - adjusted_widths.w, + p1.x - border.widths.z + adjusted_widths.z, + p1.y - border.widths.w + adjusted_widths.w); + color_delta = vec2(-1.0); + break; + } + case 3: { + p0 = vec2(corners.bl_outer.x, corners.bl_inner.y); + p1 = vec2(corners.bl_inner.x, corners.bl_outer.y); + color0 = border.colors[3]; + color1 = border.colors[0]; + vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w); + vClipSign = vec2(1.0, -1.0); + style = select_style(prim.user_data1, border.style.xw); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[1].zw, + border.widths.xw, + adjusted_widths.xw); + set_edge_line(border.widths.xw, + corners.bl_outer, + vec2(1.0, -1.0)); + edge_distances = vec4(p0.x + adjusted_widths.x, + p1.y - adjusted_widths.w, + p0.x + inv_adjusted_widths.x, + p1.y - border.widths.w + adjusted_widths.w); + color_delta = vec2(-1.0, 1.0); + break; + } + } + + switch (style) { + case BORDER_STYLE_DOUBLE: { + vEdgeDistance = edge_distances; + vAlphaSelect = 0.0; + vSDFSelect = 0.0; + break; + } + case BORDER_STYLE_GROOVE: + case BORDER_STYLE_RIDGE: + vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0); + vAlphaSelect = 1.0; + vSDFSelect = 1.0; + break; + case BORDER_STYLE_DOTTED: + // Disable normal clip radii for dotted corners, since + // all the clipping is handled by the clip mask. + vClipSign = vec2(0.0); + vEdgeDistance = vec4(0.0); + vAlphaSelect = 1.0; + vSDFSelect = 0.0; + break; + default: { + vEdgeDistance = vec4(0.0); + vAlphaSelect = 1.0; + vSDFSelect = 0.0; + break; + } + } + + write_color(color0, color1, style, color_delta, prim.user_data1); + + RectWithSize segment_rect; + segment_rect.p0 = p0; + segment_rect.size = p1 - p0; + +#ifdef WR_FEATURE_TRANSFORM + TransformVertexInfo vi = write_transform_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#else + VertexInfo vi = write_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#endif + + vLocalPos = vi.local_pos; + write_clip(vi.screen_pos, prim.clip_area); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void main(void) { + float alpha = 1.0; +#ifdef WR_FEATURE_TRANSFORM + alpha = 0.0; + vec2 local_pos = init_transform_fs(vLocalPos, alpha); +#else + vec2 local_pos = vLocalPos; +#endif + + alpha = min(alpha, do_clip()); + + // Find the appropriate distance to apply the AA smoothstep over. + vec2 fw = fwidth(local_pos); + float afwidth = length(fw); + float distance_for_color; + float color_mix_factor; + + // Only apply the clip AA if inside the clip region. This is + // necessary for correctness when the border width is greater + // than the border radius. + if (all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) { + vec2 p = local_pos - vClipCenter; + + // Get signed distance from the inner/outer clips. + float d0 = distance_to_ellipse(p, vRadii0.xy); + float d1 = distance_to_ellipse(p, vRadii0.zw); + float d2 = distance_to_ellipse(p, vRadii1.xy); + float d3 = distance_to_ellipse(p, vRadii1.zw); + + // SDF subtract main radii + float d_main = max(d0, 0.5 * afwidth - d1); + + // SDF subtract inner radii (double style borders) + float d_inner = max(d2 - 0.5 * afwidth, -d3); + + // Select how to combine the SDF based on border style. + float d = mix(max(d_main, -d_inner), d_main, vSDFSelect); + + // Only apply AA to fragments outside the signed distance field. + alpha = min(alpha, 1.0 - smoothstep(0.0, 0.5 * afwidth, d)); + + // Get the groove/ridge mix factor. + color_mix_factor = smoothstep(-0.5 * afwidth, + 0.5 * afwidth, + -d2); + } else { + // Handle the case where the fragment is outside the clip + // region in a corner. This occurs when border width is + // greater than border radius. + + // Get linear distances along horizontal and vertical edges. + vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz); + vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw); + // Apply union to get the outer edge signed distance. + float da = min(d0.x, d1.x); + // Apply intersection to get the inner edge signed distance. + float db = max(-d0.y, -d1.y); + // Apply union to get both edges. + float d = min(da, db); + // Select fragment on/off based on signed distance. + // No AA here, since we know we're on a straight edge + // and the width is rounded to a whole CSS pixel. + alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); + + // Get the groove/ridge mix factor. + // TODO(gw): Support AA for groove/ridge border edge with transforms. + color_mix_factor = mix(0.0, 1.0, da > 0.0); + } + + // Mix inner/outer color. + vec4 color0 = mix(vColor00, vColor01, color_mix_factor); + vec4 color1 = mix(vColor10, vColor11, color_mix_factor); + + // Select color based on side of line. Get distance from the + // reference line, and then apply AA along the edge. + float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos); + float m = smoothstep(-0.5 * afwidth, 0.5 * afwidth, ld); + vec4 color = mix(color0, color1, m); + + oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); +} +#endif diff --git a/gfx/webrender/res/ps_border_corner.vs.glsl b/gfx/webrender/res/ps_border_corner.vs.glsl deleted file mode 100644 index bf8a961b4a26..000000000000 --- a/gfx/webrender/res/ps_border_corner.vs.glsl +++ /dev/null @@ -1,285 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Matches BorderCornerSide enum in border.rs -#define SIDE_BOTH 0 -#define SIDE_FIRST 1 -#define SIDE_SECOND 2 - -vec2 get_radii(vec2 radius, vec2 invalid) { - if (all(greaterThan(radius, vec2(0.0)))) { - return radius; - } - - return invalid; -} - -void set_radii(int style, - vec2 radii, - vec2 widths, - vec2 adjusted_widths) { - vRadii0.xy = get_radii(radii, 2.0 * widths); - vRadii0.zw = get_radii(radii - widths, -widths); - - switch (style) { - case BORDER_STYLE_RIDGE: - case BORDER_STYLE_GROOVE: - vRadii1.xy = radii - adjusted_widths; - // See comment in default branch - vRadii1.zw = vec2(-100.0); - break; - case BORDER_STYLE_DOUBLE: - vRadii1.xy = get_radii(radii - adjusted_widths, -widths); - vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths); - break; - default: - // These aren't needed, so we set them to some reasonably large - // negative value so later computations will discard them. This - // avoids branches and numerical issues in the fragment shader. - vRadii1.xy = vec2(-100.0); - vRadii1.zw = vec2(-100.0); - break; - } -} - -void set_edge_line(vec2 border_width, - vec2 outer_corner, - vec2 gradient_sign) { - vec2 gradient = border_width * gradient_sign; - vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x)); -} - -void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) { - vec4 modulate; - - switch (style) { - case BORDER_STYLE_GROOVE: - modulate = vec4(1.0 - 0.3 * delta.x, - 1.0 + 0.3 * delta.x, - 1.0 - 0.3 * delta.y, - 1.0 + 0.3 * delta.y); - - break; - case BORDER_STYLE_RIDGE: - modulate = vec4(1.0 + 0.3 * delta.x, - 1.0 - 0.3 * delta.x, - 1.0 + 0.3 * delta.y, - 1.0 - 0.3 * delta.y); - break; - default: - modulate = vec4(1.0); - break; - } - - // Optionally mask out one side of the border corner, - // depending on the instance kind. - switch (instance_kind) { - case SIDE_FIRST: - color0.a = 0.0; - break; - case SIDE_SECOND: - color1.a = 0.0; - break; - } - - vColor00 = vec4(color0.rgb * modulate.x, color0.a); - vColor01 = vec4(color0.rgb * modulate.y, color0.a); - vColor10 = vec4(color1.rgb * modulate.z, color1.a); - vColor11 = vec4(color1.rgb * modulate.w, color1.a); -} - -int select_style(int color_select, vec2 fstyle) { - ivec2 style = ivec2(fstyle); - - switch (color_select) { - case SIDE_BOTH: - { - // TODO(gw): A temporary hack! While we don't support - // border corners that have dots or dashes - // with another style, pretend they are solid - // border corners. - bool has_dots = style.x == BORDER_STYLE_DOTTED || - style.y == BORDER_STYLE_DOTTED; - bool has_dashes = style.x == BORDER_STYLE_DASHED || - style.y == BORDER_STYLE_DASHED; - if (style.x != style.y && (has_dots || has_dashes)) - return BORDER_STYLE_SOLID; - return style.x; - } - case SIDE_FIRST: - return style.x; - case SIDE_SECOND: - return style.y; - } -} - -void main(void) { - Primitive prim = load_primitive(); - Border border = fetch_border(prim.specific_prim_address); - int sub_part = prim.user_data0; - BorderCorners corners = get_border_corners(border, prim.local_rect); - - vec2 p0, p1; - - // TODO(gw): We'll need to pass through multiple styles - // once we support style transitions per corner. - int style; - vec4 edge_distances; - vec4 color0, color1; - vec2 color_delta; - - // TODO(gw): Now that all border styles are supported, the switch - // statement below can be tidied up quite a bit. - - switch (sub_part) { - case 0: { - p0 = corners.tl_outer; - p1 = corners.tl_inner; - color0 = border.colors[0]; - color1 = border.colors[1]; - vClipCenter = corners.tl_outer + border.radii[0].xy; - vClipSign = vec2(1.0); - style = select_style(prim.user_data1, border.style.yx); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[0].xy, - border.widths.xy, - adjusted_widths.xy); - set_edge_line(border.widths.xy, - corners.tl_outer, - vec2(1.0, 1.0)); - edge_distances = vec4(p0 + adjusted_widths.xy, - p0 + inv_adjusted_widths.xy); - color_delta = vec2(1.0); - break; - } - case 1: { - p0 = vec2(corners.tr_inner.x, corners.tr_outer.y); - p1 = vec2(corners.tr_outer.x, corners.tr_inner.y); - color0 = border.colors[1]; - color1 = border.colors[2]; - vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w); - vClipSign = vec2(-1.0, 1.0); - style = select_style(prim.user_data1, border.style.zy); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[0].zw, - border.widths.zy, - adjusted_widths.zy); - set_edge_line(border.widths.zy, - corners.tr_outer, - vec2(-1.0, 1.0)); - edge_distances = vec4(p1.x - adjusted_widths.z, - p0.y + adjusted_widths.y, - p1.x - border.widths.z + adjusted_widths.z, - p0.y + inv_adjusted_widths.y); - color_delta = vec2(1.0, -1.0); - break; - } - case 2: { - p0 = corners.br_inner; - p1 = corners.br_outer; - color0 = border.colors[2]; - color1 = border.colors[3]; - vClipCenter = corners.br_outer - border.radii[1].xy; - vClipSign = vec2(-1.0, -1.0); - style = select_style(prim.user_data1, border.style.wz); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[1].xy, - border.widths.zw, - adjusted_widths.zw); - set_edge_line(border.widths.zw, - corners.br_outer, - vec2(-1.0, -1.0)); - edge_distances = vec4(p1.x - adjusted_widths.z, - p1.y - adjusted_widths.w, - p1.x - border.widths.z + adjusted_widths.z, - p1.y - border.widths.w + adjusted_widths.w); - color_delta = vec2(-1.0); - break; - } - case 3: { - p0 = vec2(corners.bl_outer.x, corners.bl_inner.y); - p1 = vec2(corners.bl_inner.x, corners.bl_outer.y); - color0 = border.colors[3]; - color1 = border.colors[0]; - vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w); - vClipSign = vec2(1.0, -1.0); - style = select_style(prim.user_data1, border.style.xw); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[1].zw, - border.widths.xw, - adjusted_widths.xw); - set_edge_line(border.widths.xw, - corners.bl_outer, - vec2(1.0, -1.0)); - edge_distances = vec4(p0.x + adjusted_widths.x, - p1.y - adjusted_widths.w, - p0.x + inv_adjusted_widths.x, - p1.y - border.widths.w + adjusted_widths.w); - color_delta = vec2(-1.0, 1.0); - break; - } - } - - switch (style) { - case BORDER_STYLE_DOUBLE: { - vEdgeDistance = edge_distances; - vAlphaSelect = 0.0; - vSDFSelect = 0.0; - break; - } - case BORDER_STYLE_GROOVE: - case BORDER_STYLE_RIDGE: - vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0); - vAlphaSelect = 1.0; - vSDFSelect = 1.0; - break; - case BORDER_STYLE_DOTTED: - // Disable normal clip radii for dotted corners, since - // all the clipping is handled by the clip mask. - vClipSign = vec2(0.0); - vEdgeDistance = vec4(0.0); - vAlphaSelect = 1.0; - vSDFSelect = 0.0; - break; - default: { - vEdgeDistance = vec4(0.0); - vAlphaSelect = 1.0; - vSDFSelect = 0.0; - break; - } - } - - write_color(color0, color1, style, color_delta, prim.user_data1); - - RectWithSize segment_rect; - segment_rect.p0 = p0; - segment_rect.size = p1 - p0; - -#ifdef WR_FEATURE_TRANSFORM - TransformVertexInfo vi = write_transform_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#else - VertexInfo vi = write_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#endif - - vLocalPos = vi.local_pos; - write_clip(vi.screen_pos, prim.clip_area); -} diff --git a/gfx/webrender/res/ps_border_edge.fs.glsl b/gfx/webrender/res/ps_border_edge.fs.glsl deleted file mode 100644 index d59d214e6d4b..000000000000 --- a/gfx/webrender/res/ps_border_edge.fs.glsl +++ /dev/null @@ -1,63 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void main(void) { - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - alpha = 0.0; - vec2 local_pos = init_transform_fs(vLocalPos, alpha); -#else - vec2 local_pos = vLocalPos; -#endif - - alpha = min(alpha, do_clip()); - - // Find the appropriate distance to apply the step over. - vec2 fw = fwidth(local_pos); - float afwidth = length(fw); - - // Applies the math necessary to draw a style: double - // border. In the case of a solid border, the vertex - // shader sets interpolator values that make this have - // no effect. - - // Select the x/y coord, depending on which axis this edge is. - vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect); - - // Get signed distance from each of the inner edges. - float d0 = pos.x - vEdgeDistance.x; - float d1 = vEdgeDistance.y - pos.x; - - // SDF union to select both outer edges. - float d = min(d0, d1); - - // Select fragment on/off based on signed distance. - // No AA here, since we know we're on a straight edge - // and the width is rounded to a whole CSS pixel. - alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); - - // Mix color based on first distance. - // TODO(gw): Support AA for groove/ridge border edge with transforms. - vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0)); - - // Apply dashing / dotting parameters. - - // Get the main-axis position relative to closest dot or dash. - float x = mod(pos.y - vClipParams.x, vClipParams.y); - - // Calculate dash alpha (on/off) based on dash length - float dash_alpha = step(x, vClipParams.z); - - // Get the dot alpha - vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw; - float dot_distance = length(dot_relative_pos) - vClipParams.z; - float dot_alpha = 1.0 - smoothstep(-0.5 * afwidth, - 0.5 * afwidth, - dot_distance); - - // Select between dot/dash alpha based on clip mode. - alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect)); - - oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); -} diff --git a/gfx/webrender/res/ps_border_edge.glsl b/gfx/webrender/res/ps_border_edge.glsl index 294b9422a1b7..8c72d2839880 100644 --- a/gfx/webrender/res/ps_border_edge.glsl +++ b/gfx/webrender/res/ps_border_edge.glsl @@ -17,3 +17,287 @@ varying vec3 vLocalPos; #else varying vec2 vLocalPos; #endif + +#ifdef WR_VERTEX_SHADER +void write_edge_distance(float p0, + float original_width, + float adjusted_width, + float style, + float axis_select, + float sign_adjust) { + switch (int(style)) { + case BORDER_STYLE_DOUBLE: + vEdgeDistance = vec2(p0 + adjusted_width, + p0 + original_width - adjusted_width); + break; + case BORDER_STYLE_GROOVE: + case BORDER_STYLE_RIDGE: + vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust); + break; + default: + vEdgeDistance = vec2(0.0); + break; + } + + vAxisSelect = axis_select; +} + +void write_alpha_select(float style) { + switch (int(style)) { + case BORDER_STYLE_DOUBLE: + vAlphaSelect = 0.0; + break; + default: + vAlphaSelect = 1.0; + break; + } +} + +// write_color function is duplicated to work around a Mali-T880 GPU driver program link error. +// See https://github.com/servo/webrender/issues/1403 for more info. +// TODO: convert back to a single function once the driver issues are resolved, if ever. +void write_color0(vec4 color, float style, bool flip) { + vec2 modulate; + + switch (int(style)) { + case BORDER_STYLE_GROOVE: + { + modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); + break; + } + case BORDER_STYLE_RIDGE: + { + modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); + break; + } + default: + modulate = vec2(1.0); + break; + } + + vColor0 = vec4(color.rgb * modulate.x, color.a); +} + +void write_color1(vec4 color, float style, bool flip) { + vec2 modulate; + + switch (int(style)) { + case BORDER_STYLE_GROOVE: + { + modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); + break; + } + case BORDER_STYLE_RIDGE: + { + modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); + break; + } + default: + modulate = vec2(1.0); + break; + } + + vColor1 = vec4(color.rgb * modulate.y, color.a); +} + +void write_clip_params(float style, + float border_width, + float edge_length, + float edge_offset, + float center_line) { + // x = offset + // y = dash on + off length + // z = dash length + // w = center line of edge cross-axis (for dots only) + switch (int(style)) { + case BORDER_STYLE_DASHED: { + float desired_dash_length = border_width * 3.0; + // Consider half total length since there is an equal on/off for each dash. + float dash_count = ceil(0.5 * edge_length / desired_dash_length); + float dash_length = 0.5 * edge_length / dash_count; + vClipParams = vec4(edge_offset - 0.5 * dash_length, + 2.0 * dash_length, + dash_length, + 0.0); + vClipSelect = 0.0; + break; + } + case BORDER_STYLE_DOTTED: { + float diameter = border_width; + float radius = 0.5 * diameter; + float dot_count = ceil(0.5 * edge_length / diameter); + float empty_space = edge_length - dot_count * diameter; + float distance_between_centers = diameter + empty_space / dot_count; + vClipParams = vec4(edge_offset - radius, + distance_between_centers, + radius, + center_line); + vClipSelect = 1.0; + break; + } + default: + vClipParams = vec4(1.0); + vClipSelect = 0.0; + break; + } +} + +void main(void) { + Primitive prim = load_primitive(); + Border border = fetch_border(prim.specific_prim_address); + int sub_part = prim.user_data0; + BorderCorners corners = get_border_corners(border, prim.local_rect); + vec4 color = border.colors[sub_part]; + + // TODO(gw): Now that all border styles are supported, the switch + // statement below can be tidied up quite a bit. + + float style; + bool color_flip; + + RectWithSize segment_rect; + switch (sub_part) { + case 0: { + segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y); + segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x)); + write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0); + style = border.style.x; + color_flip = false; + write_clip_params(border.style.x, + border.widths.x, + segment_rect.size.y, + segment_rect.p0.y, + segment_rect.p0.x + 0.5 * segment_rect.size.x); + break; + } + case 1: { + segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y); + segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y)); + write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0); + style = border.style.y; + color_flip = false; + write_clip_params(border.style.y, + border.widths.y, + segment_rect.size.x, + segment_rect.p0.x, + segment_rect.p0.y + 0.5 * segment_rect.size.y); + break; + } + case 2: { + segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y); + segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z)); + write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0); + style = border.style.z; + color_flip = true; + write_clip_params(border.style.z, + border.widths.z, + segment_rect.size.y, + segment_rect.p0.y, + segment_rect.p0.x + 0.5 * segment_rect.size.x); + break; + } + case 3: { + segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w); + segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w)); + write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0); + style = border.style.w; + color_flip = true; + write_clip_params(border.style.w, + border.widths.w, + segment_rect.size.x, + segment_rect.p0.x, + segment_rect.p0.y + 0.5 * segment_rect.size.y); + break; + } + } + + write_alpha_select(style); + write_color0(color, style, color_flip); + write_color1(color, style, color_flip); + +#ifdef WR_FEATURE_TRANSFORM + TransformVertexInfo vi = write_transform_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#else + VertexInfo vi = write_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#endif + + vLocalPos = vi.local_pos; + write_clip(vi.screen_pos, prim.clip_area); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void main(void) { + float alpha = 1.0; +#ifdef WR_FEATURE_TRANSFORM + alpha = 0.0; + vec2 local_pos = init_transform_fs(vLocalPos, alpha); +#else + vec2 local_pos = vLocalPos; +#endif + + alpha = min(alpha, do_clip()); + + // Find the appropriate distance to apply the step over. + vec2 fw = fwidth(local_pos); + float afwidth = length(fw); + + // Applies the math necessary to draw a style: double + // border. In the case of a solid border, the vertex + // shader sets interpolator values that make this have + // no effect. + + // Select the x/y coord, depending on which axis this edge is. + vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect); + + // Get signed distance from each of the inner edges. + float d0 = pos.x - vEdgeDistance.x; + float d1 = vEdgeDistance.y - pos.x; + + // SDF union to select both outer edges. + float d = min(d0, d1); + + // Select fragment on/off based on signed distance. + // No AA here, since we know we're on a straight edge + // and the width is rounded to a whole CSS pixel. + alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); + + // Mix color based on first distance. + // TODO(gw): Support AA for groove/ridge border edge with transforms. + vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0)); + + // Apply dashing / dotting parameters. + + // Get the main-axis position relative to closest dot or dash. + float x = mod(pos.y - vClipParams.x, vClipParams.y); + + // Calculate dash alpha (on/off) based on dash length + float dash_alpha = step(x, vClipParams.z); + + // Get the dot alpha + vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw; + float dot_distance = length(dot_relative_pos) - vClipParams.z; + float dot_alpha = 1.0 - smoothstep(-0.5 * afwidth, + 0.5 * afwidth, + dot_distance); + + // Select between dot/dash alpha based on clip mode. + alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect)); + + oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); +} +#endif diff --git a/gfx/webrender/res/ps_border_edge.vs.glsl b/gfx/webrender/res/ps_border_edge.vs.glsl deleted file mode 100644 index ef3349d36111..000000000000 --- a/gfx/webrender/res/ps_border_edge.vs.glsl +++ /dev/null @@ -1,223 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void write_edge_distance(float p0, - float original_width, - float adjusted_width, - float style, - float axis_select, - float sign_adjust) { - switch (int(style)) { - case BORDER_STYLE_DOUBLE: - vEdgeDistance = vec2(p0 + adjusted_width, - p0 + original_width - adjusted_width); - break; - case BORDER_STYLE_GROOVE: - case BORDER_STYLE_RIDGE: - vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust); - break; - default: - vEdgeDistance = vec2(0.0); - break; - } - - vAxisSelect = axis_select; -} - -void write_alpha_select(float style) { - switch (int(style)) { - case BORDER_STYLE_DOUBLE: - vAlphaSelect = 0.0; - break; - default: - vAlphaSelect = 1.0; - break; - } -} - -// write_color function is duplicated to work around a Mali-T880 GPU driver program link error. -// See https://github.com/servo/webrender/issues/1403 for more info. -// TODO: convert back to a single function once the driver issues are resolved, if ever. -void write_color0(vec4 color, float style, bool flip) { - vec2 modulate; - - switch (int(style)) { - case BORDER_STYLE_GROOVE: - { - modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); - break; - } - case BORDER_STYLE_RIDGE: - { - modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); - break; - } - default: - modulate = vec2(1.0); - break; - } - - vColor0 = vec4(color.rgb * modulate.x, color.a); -} - -void write_color1(vec4 color, float style, bool flip) { - vec2 modulate; - - switch (int(style)) { - case BORDER_STYLE_GROOVE: - { - modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); - break; - } - case BORDER_STYLE_RIDGE: - { - modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); - break; - } - default: - modulate = vec2(1.0); - break; - } - - vColor1 = vec4(color.rgb * modulate.y, color.a); -} - -void write_clip_params(float style, - float border_width, - float edge_length, - float edge_offset, - float center_line) { - // x = offset - // y = dash on + off length - // z = dash length - // w = center line of edge cross-axis (for dots only) - switch (int(style)) { - case BORDER_STYLE_DASHED: { - float desired_dash_length = border_width * 3.0; - // Consider half total length since there is an equal on/off for each dash. - float dash_count = ceil(0.5 * edge_length / desired_dash_length); - float dash_length = 0.5 * edge_length / dash_count; - vClipParams = vec4(edge_offset - 0.5 * dash_length, - 2.0 * dash_length, - dash_length, - 0.0); - vClipSelect = 0.0; - break; - } - case BORDER_STYLE_DOTTED: { - float diameter = border_width; - float radius = 0.5 * diameter; - float dot_count = ceil(0.5 * edge_length / diameter); - float empty_space = edge_length - dot_count * diameter; - float distance_between_centers = diameter + empty_space / dot_count; - vClipParams = vec4(edge_offset - radius, - distance_between_centers, - radius, - center_line); - vClipSelect = 1.0; - break; - } - default: - vClipParams = vec4(1.0); - vClipSelect = 0.0; - break; - } -} - -void main(void) { - Primitive prim = load_primitive(); - Border border = fetch_border(prim.specific_prim_address); - int sub_part = prim.user_data0; - BorderCorners corners = get_border_corners(border, prim.local_rect); - vec4 color = border.colors[sub_part]; - - // TODO(gw): Now that all border styles are supported, the switch - // statement below can be tidied up quite a bit. - - float style; - bool color_flip; - - RectWithSize segment_rect; - switch (sub_part) { - case 0: { - segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y); - segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x)); - write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0); - style = border.style.x; - color_flip = false; - write_clip_params(border.style.x, - border.widths.x, - segment_rect.size.y, - segment_rect.p0.y, - segment_rect.p0.x + 0.5 * segment_rect.size.x); - break; - } - case 1: { - segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y); - segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y)); - write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0); - style = border.style.y; - color_flip = false; - write_clip_params(border.style.y, - border.widths.y, - segment_rect.size.x, - segment_rect.p0.x, - segment_rect.p0.y + 0.5 * segment_rect.size.y); - break; - } - case 2: { - segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y); - segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z)); - write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0); - style = border.style.z; - color_flip = true; - write_clip_params(border.style.z, - border.widths.z, - segment_rect.size.y, - segment_rect.p0.y, - segment_rect.p0.x + 0.5 * segment_rect.size.x); - break; - } - case 3: { - segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w); - segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w)); - write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0); - style = border.style.w; - color_flip = true; - write_clip_params(border.style.w, - border.widths.w, - segment_rect.size.x, - segment_rect.p0.x, - segment_rect.p0.y + 0.5 * segment_rect.size.y); - break; - } - } - - write_alpha_select(style); - write_color0(color, style, color_flip); - write_color1(color, style, color_flip); - -#ifdef WR_FEATURE_TRANSFORM - TransformVertexInfo vi = write_transform_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#else - VertexInfo vi = write_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#endif - - vLocalPos = vi.local_pos; - write_clip(vi.screen_pos, prim.clip_area); -} diff --git a/gfx/webrender/res/ps_box_shadow.fs.glsl b/gfx/webrender/res/ps_box_shadow.fs.glsl index 1bbaedf9eb17..26fbff4fd79e 100644 --- a/gfx/webrender/res/ps_box_shadow.fs.glsl +++ b/gfx/webrender/res/ps_box_shadow.fs.glsl @@ -18,5 +18,6 @@ void main(void) { uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv); // Modulate the box shadow by the color. - oFragColor = clip_scale * dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z))); + float mask = texture(sColor1, vec3(uv, vUv.z)).r; + oFragColor = clip_scale * dither(vColor * vec4(1.0, 1.0, 1.0, mask)); } diff --git a/gfx/webrender/res/ps_box_shadow.vs.glsl b/gfx/webrender/res/ps_box_shadow.vs.glsl index 6f4b8695a13c..7ac47a6a92ae 100644 --- a/gfx/webrender/res/ps_box_shadow.vs.glsl +++ b/gfx/webrender/res/ps_box_shadow.vs.glsl @@ -27,7 +27,7 @@ void main(void) { vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size; vMirrorPoint = 0.5 * prim.local_rect.size / patch_size; - vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0)); + vec2 texture_size = vec2(textureSize(sCacheA8, 0)); vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy; vColor = bs.color; diff --git a/gfx/webrender/src/debug_server.rs b/gfx/webrender/src/debug_server.rs index 4078f5353bf8..92d650cc39f5 100644 --- a/gfx/webrender/src/debug_server.rs +++ b/gfx/webrender/src/debug_server.rs @@ -14,8 +14,9 @@ use ws; // debug command queue. These are sent in a separate queue so // that none of these types are exposed to the RenderApi interfaces. // We can't use select!() as it's not stable... -pub enum DebugMsg { - FetchPasses(ws::Sender), +enum DebugMsg { + AddSender(ws::Sender), + RemoveSender(ws::util::Token), } // Represents a connection to a client. @@ -26,6 +27,16 @@ struct Server { } impl ws::Handler for Server { + fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> { + self.debug_tx.send(DebugMsg::AddSender(self.ws.clone())).ok(); + + Ok(()) + } + + fn on_close(&mut self, _: ws::CloseCode, _: &str) { + self.debug_tx.send(DebugMsg::RemoveSender(self.ws.token())).ok(); + } + fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { match msg { ws::Message::Text(string) => { @@ -49,9 +60,10 @@ impl ws::Handler for Server { DebugCommand::EnableRenderTargetDebug(false) } "fetch_passes" => { - let msg = DebugMsg::FetchPasses(self.ws.clone()); - self.debug_tx.send(msg).unwrap(); - DebugCommand::Flush + DebugCommand::FetchPasses + } + "fetch_documents" => { + DebugCommand::FetchDocuments } msg => { println!("unknown msg {}", msg); @@ -74,7 +86,8 @@ impl ws::Handler for Server { pub struct DebugServer { join_handle: Option>, broadcaster: ws::Sender, - pub debug_rx: Receiver, + debug_rx: Receiver, + senders: Vec, } impl DebugServer { @@ -101,6 +114,42 @@ impl DebugServer { join_handle, broadcaster, debug_rx, + senders: Vec::new(), + } + } + + pub fn send(&mut self, message: String) { + // Add any new connections that have been queued. + while let Ok(msg) = self.debug_rx.try_recv() { + match msg { + DebugMsg::AddSender(sender) => { + self.senders.push(sender); + } + DebugMsg::RemoveSender(token) => { + self.senders.retain(|sender| { + sender.token() != token + }); + } + } + } + + // Broadcast the message to all senders. Keep + // track of the ones that failed, so they can + // be removed from the active sender list. + let mut disconnected_senders = Vec::new(); + + for (i, sender) in self.senders.iter().enumerate() { + if let Err(..) = sender.send(message.clone()) { + disconnected_senders.push(i); + } + } + + // Remove the broken senders from the list + // for next broadcast. Remove in reverse + // order so the indices are valid for the + // entire loop. + for i in disconnected_senders.iter().rev() { + self.senders.remove(*i); } } } @@ -190,3 +239,45 @@ struct Batch { description: String, count: usize, } + +#[derive(Serialize)] +pub struct TreeNode { + description: String, + children: Vec, +} + +impl TreeNode { + pub fn new(description: &str) -> TreeNode { + TreeNode { + description: description.to_owned(), + children: Vec::new(), + } + } + + pub fn add_child(&mut self, child: TreeNode) { + self.children.push(child); + } + + pub fn add_item(&mut self, description: &str) { + self.children.push(TreeNode::new(description)); + } +} + +#[derive(Serialize)] +pub struct DocumentList { + kind: &'static str, + root: TreeNode, +} + +impl DocumentList { + pub fn new() -> DocumentList { + DocumentList { + kind: "documents", + root: TreeNode::new("root"), + } + } + + pub fn add(&mut self, item: TreeNode) { + self.root.add_child(item); + } +} diff --git a/gfx/webrender/src/device.rs b/gfx/webrender/src/device.rs index 7a708409d78c..807e2abcf550 100644 --- a/gfx/webrender/src/device.rs +++ b/gfx/webrender/src/device.rs @@ -1176,6 +1176,7 @@ impl Device { pub fn free_texture_storage(&mut self, texture: &mut Texture) { debug_assert!(self.inside_frame); + debug_assert_eq!(self.bound_pbo, PBOId(0)); if texture.format == ImageFormat::Invalid { return; @@ -1373,7 +1374,7 @@ impl Device { pub fn update_pbo_data(&mut self, data: &[T]) { debug_assert!(self.inside_frame); - debug_assert!(self.bound_pbo.0 != 0); + debug_assert_ne!(self.bound_pbo, PBOId(0)); gl::buffer_data(&*self.gl, gl::PIXEL_UNPACK_BUFFER, @@ -1383,7 +1384,7 @@ impl Device { pub fn orphan_pbo(&mut self, new_size: usize) { debug_assert!(self.inside_frame); - debug_assert!(self.bound_pbo.0 != 0); + debug_assert_ne!(self.bound_pbo, PBOId(0)); self.gl.buffer_data_untyped(gl::PIXEL_UNPACK_BUFFER, new_size as isize, diff --git a/gfx/webrender/src/frame.rs b/gfx/webrender/src/frame.rs index e12e30501c2e..6ca3f3cb7b64 100644 --- a/gfx/webrender/src/frame.rs +++ b/gfx/webrender/src/frame.rs @@ -95,6 +95,7 @@ impl NestedDisplayListInfo { struct FlattenContext<'a> { scene: &'a Scene, builder: &'a mut FrameBuilder, + resource_cache: &'a ResourceCache, tiled_image_map: TiledImageMap, replacements: Vec<(ClipId, ClipId)>, nested_display_list_info: Vec, @@ -104,11 +105,12 @@ struct FlattenContext<'a> { impl<'a> FlattenContext<'a> { fn new(scene: &'a Scene, builder: &'a mut FrameBuilder, - resource_cache: &ResourceCache) + resource_cache: &'a ResourceCache) -> FlattenContext<'a> { FlattenContext { scene, builder, + resource_cache, tiled_image_map: resource_cache.get_tiled_image_map(), replacements: Vec::new(), nested_display_list_info: Vec::new(), @@ -565,12 +567,12 @@ impl Frame { info.image_rendering); } SpecificDisplayItem::Text(ref text_info) => { + let instance = context.resource_cache.get_font_instance(text_info.font_key).unwrap(); context.builder.add_text(clip_and_scroll, reference_frame_relative_offset, item_rect_with_offset, &clip_with_offset, - text_info.font_key, - text_info.size, + instance, &text_info.color, item.glyphs(), item.display_list().get(item.glyphs()).count(), diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index 9fcd8dea9515..bdad0d3dd64f 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -4,7 +4,8 @@ use api::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo, ClipId, ColorF}; use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize}; -use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop}; +use api::{ExtendMode, FontInstance, FontRenderMode}; +use api::{GlyphInstance, GlyphOptions, GradientStop}; use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize}; use api::{LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation, LineStyle}; use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, SubpixelDirection, TextShadow}; @@ -875,24 +876,37 @@ impl FrameBuilder { run_offset: LayoutVector2D, rect: LayerRect, local_clip: &LocalClip, - font_key: FontKey, - size: Au, + font: &FontInstance, color: &ColorF, glyph_range: ItemRange, glyph_count: usize, glyph_options: Option) { // Trivial early out checks - if size.0 <= 0 { + if font.size.0 <= 0 { + return + } + + // Sanity check - anything with glyphs bigger than this + // is probably going to consume too much memory to render + // efficiently anyway. This is specifically to work around + // the font_advance.html reftest, which creates a very large + // font as a crash test - the rendering is also ignored + // by the azure renderer. + if font.size >= Au::from_px(4096) { return } // TODO(gw): Use a proper algorithm to select // whether this item should be rendered with // subpixel AA! - let mut normal_render_mode = self.config.default_font_render_mode; + let mut default_render_mode = self.config.default_font_render_mode.limit_by(font.render_mode); + if let Some(options) = glyph_options { + default_render_mode = default_render_mode.limit_by(options.render_mode); + } // There are some conditions under which we can't use // subpixel text rendering, even if enabled. + let mut normal_render_mode = default_render_mode; if normal_render_mode == FontRenderMode::Subpixel { if color.a != 1.0 { normal_render_mode = FontRenderMode::Alpha; @@ -913,30 +927,32 @@ impl FrameBuilder { // Shadows never use subpixel AA, but need to respect the alpha/mono flag // for reftests. - let (shadow_render_mode, subpx_dir) = match self.config.default_font_render_mode { + let (shadow_render_mode, subpx_dir) = match default_render_mode { FontRenderMode::Subpixel | FontRenderMode::Alpha => { // TODO(gw): Expose subpixel direction in API once WR supports // vertical text runs. - (FontRenderMode::Alpha, SubpixelDirection::Horizontal) + (FontRenderMode::Alpha, font.subpx_dir) } FontRenderMode::Mono => { (FontRenderMode::Mono, SubpixelDirection::None) } }; + let prim_font = FontInstance::new(font.font_key, + font.size, + *color, + normal_render_mode, + subpx_dir, + font.platform_options); let prim = TextRunPrimitiveCpu { - font_key, - logical_font_size: size, + font: prim_font, glyph_range, glyph_count, glyph_gpu_blocks: Vec::new(), glyph_keys: Vec::new(), - glyph_options, - normal_render_mode, shadow_render_mode, offset: run_offset, color: *color, - subpx_dir, }; // Text shadows that have a blur radius of 0 need to be rendered as normal @@ -953,6 +969,7 @@ impl FrameBuilder { let shadow_prim = &self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0]; if shadow_prim.shadow.blur_radius == 0.0 { let mut text_prim = prim.clone(); + text_prim.font.color = shadow_prim.shadow.color.into(); text_prim.color = shadow_prim.shadow.color; text_prim.offset += shadow_prim.shadow.offset; fast_text_shadow_prims.push(text_prim); diff --git a/gfx/webrender/src/glyph_rasterizer.rs b/gfx/webrender/src/glyph_rasterizer.rs index 5cb73579b33d..0d04f3d846b5 100644 --- a/gfx/webrender/src/glyph_rasterizer.rs +++ b/gfx/webrender/src/glyph_rasterizer.rs @@ -378,14 +378,12 @@ fn raterize_200_glyphs() { let font_key = FontKey::new(IdNamespace(0), 0); glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0)); - let font = FontInstance { - font_key, - color: ColorF::new(0.0, 0.0, 0.0, 1.0).into(), - size: Au::from_px(32), - render_mode: FontRenderMode::Subpixel, - glyph_options: None, - subpx_dir: SubpixelDirection::Horizontal, - }; + let font = FontInstance::new(font_key, + Au::from_px(32), + ColorF::new(0.0, 0.0, 0.0, 1.0), + FontRenderMode::Subpixel, + SubpixelDirection::Horizontal, + None); let mut glyph_keys = Vec::with_capacity(200); for i in 0..200 { diff --git a/gfx/webrender/src/internal_types.rs b/gfx/webrender/src/internal_types.rs index 6ed9266058f2..2eaa3b1cce87 100644 --- a/gfx/webrender/src/internal_types.rs +++ b/gfx/webrender/src/internal_types.rs @@ -69,7 +69,7 @@ impl BatchTextures { BatchTextures { colors: [ SourceTexture::CacheRGBA8, - SourceTexture::Invalid, + SourceTexture::CacheA8, SourceTexture::Invalid, ] } @@ -156,8 +156,13 @@ impl RendererFrame { } } +pub enum DebugOutput { + FetchDocuments(String), +} + pub enum ResultMsg { DebugCommand(DebugCommand), + DebugOutput(DebugOutput), RefreshShader(PathBuf), NewFrame(DocumentId, RendererFrame, TextureUpdateList, BackendProfileCounters), UpdateResources { updates: TextureUpdateList, cancel_rendering: bool }, diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index d84bca9eaf52..2658918f8f60 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{FontKey, FontRenderMode, GlyphDimensions}; -use api::{FontInstance, GlyphKey, GlyphOptions, SubpixelDirection}; +use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode}; +use api::{GlyphDimensions, GlyphKey, GlyphOptions, SubpixelDirection}; use gamma_lut::{GammaLut, Color as ColorLut}; use internal_types::FastHashMap; @@ -46,9 +46,9 @@ fn dwrite_texture_type(render_mode: FontRenderMode) -> } } -fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option) -> +fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option) -> dwrote::DWRITE_MEASURING_MODE { - if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options { + if let Some(FontInstancePlatformOptions{ force_gdi_rendering: true, .. }) = options { return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC; } @@ -63,9 +63,9 @@ fn dwrite_render_mode(font_face: &dwrote::FontFace, render_mode: FontRenderMode, em_size: f32, measure_mode: dwrote::DWRITE_MEASURING_MODE, - options: Option) -> + options: Option) -> dwrote::DWRITE_RENDERING_MODE { - if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options { + if let Some(FontInstancePlatformOptions{ force_gdi_rendering: true, .. }) = options { return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC; } @@ -175,12 +175,12 @@ impl FontContext { }; let dwrite_measure_mode = dwrite_measure_mode(font.render_mode, - font.glyph_options); + font.platform_options); let dwrite_render_mode = dwrite_render_mode(face, font.render_mode, font.size.to_f32_px(), dwrite_measure_mode, - font.glyph_options); + font.platform_options); let (x_offset, y_offset) = font.get_subpx_offset(key); let transform = Some( @@ -302,7 +302,7 @@ impl FontContext { let mut pixels = analysis.create_alpha_texture(texture_type, bounds); if font.render_mode != FontRenderMode::Mono { - let lut_correction = match font.glyph_options { + let lut_correction = match font.platform_options { Some(option) => { if option.force_gdi_rendering { &self.gdi_gamma_lut diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 1aec0e36cc66..a85595be0368 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -3,10 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize, DevicePoint}; -use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop}; +use api::{ExtendMode, FontRenderMode, GlyphInstance, GradientStop}; use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize, TextShadow}; use api::{GlyphKey, LayerToWorldTransform, TileOffset, YuvColorSpace, YuvFormat}; -use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle, SubpixelDirection}; +use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle}; use app_units::Au; use border::BorderCornerInstance; use euclid::{Size2D}; @@ -504,21 +504,17 @@ pub struct TextShadowPrimitiveCpu { #[derive(Debug, Clone)] pub struct TextRunPrimitiveCpu { - pub font_key: FontKey, + pub font: FontInstance, pub offset: LayerVector2D, - pub logical_font_size: Au, pub glyph_range: ItemRange, pub glyph_count: usize, pub glyph_keys: Vec, pub glyph_gpu_blocks: Vec, - pub glyph_options: Option, - pub normal_render_mode: FontRenderMode, pub shadow_render_mode: FontRenderMode, pub color: ColorF, - pub subpx_dir: SubpixelDirection, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum TextRunMode { Normal, Shadow, @@ -531,18 +527,18 @@ impl TextRunPrimitiveCpu { display_list: &BuiltDisplayList, run_mode: TextRunMode, gpu_cache: &mut GpuCache) { - let font_size_dp = self.logical_font_size.scale_by(device_pixel_ratio); - let render_mode = match run_mode { - TextRunMode::Normal => self.normal_render_mode, - TextRunMode::Shadow => self.shadow_render_mode, - }; + let mut font = self.font.clone(); + font.size = font.size.scale_by(device_pixel_ratio); + match run_mode { + TextRunMode::Shadow => { + font.render_mode = self.shadow_render_mode; + } + TextRunMode::Normal => {} + } - let font = FontInstance::new(self.font_key, - font_size_dp, - self.color, - render_mode, - self.glyph_options, - self.subpx_dir); + if run_mode == TextRunMode::Shadow { + font.render_mode = self.shadow_render_mode; + } // Cache the glyph positions, if not in the cache already. // TODO(gw): In the future, remove `glyph_instances` @@ -590,7 +586,7 @@ impl TextRunPrimitiveCpu { request.push(self.color); request.push([self.offset.x, self.offset.y, - self.subpx_dir as u32 as f32, + self.font.subpx_dir as u32 as f32, 0.0]); request.extend_from_slice(&self.glyph_gpu_blocks); diff --git a/gfx/webrender/src/profiler.rs b/gfx/webrender/src/profiler.rs index 865429f3208d..6d9a687688b7 100644 --- a/gfx/webrender/src/profiler.rs +++ b/gfx/webrender/src/profiler.rs @@ -131,6 +131,11 @@ impl ResourceProfileCounter { self.value += 1; self.size += size; } + + pub fn set(&mut self, count: usize, size: usize) { + self.value = count; + self.size = size; + } } impl ProfileCounter for ResourceProfileCounter { diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index 3c3ed90252ff..4af1c8b3fce5 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -2,14 +2,18 @@ * 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/. */ +#[cfg(feature = "debugger")] +use debug_server; use frame::Frame; use frame_builder::FrameBuilderConfig; use gpu_cache::GpuCache; -use internal_types::{FastHashMap, ResultMsg, RendererFrame}; +use internal_types::{DebugOutput, FastHashMap, ResultMsg, RendererFrame}; use profiler::{BackendProfileCounters, ResourceProfileCounters}; use record::ApiRecordingReceiver; use resource_cache::ResourceCache; use scene::Scene; +#[cfg(feature = "debugger")] +use serde_json; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; use std::u32; @@ -19,9 +23,11 @@ use thread_profiler::register_thread_with_profiler; use rayon::ThreadPool; use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods}; use api::channel::{PayloadSender, PayloadSenderHelperMethods}; -use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint}; +use api::{ApiMsg, DebugCommand, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint}; use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, DocumentMsg}; use api::{IdNamespace, LayerPoint, RenderNotifier}; +#[cfg(feature = "debugger")] +use api::{BuiltDisplayListIter, SpecificDisplayItem}; struct Document { scene: Scene, @@ -275,6 +281,12 @@ impl RenderBackend { DocumentOp::Nop } } + DocumentMsg::RemovePipeline(pipeline_id) => { + profile_scope!("RemovePipeline"); + + doc.scene.remove_pipeline(pipeline_id); + DocumentOp::Nop + } DocumentMsg::Scroll(delta, cursor, move_phase) => { profile_scope!("Scroll"); let _timer = profile_counters.total_time.timer(); @@ -462,7 +474,15 @@ impl RenderBackend { self.notifier.lock().unwrap().as_mut().unwrap().new_frame_ready(); } ApiMsg::DebugCommand(option) => { - let msg = ResultMsg::DebugCommand(option); + let msg = match option { + DebugCommand::FetchDocuments => { + let json = self.get_docs_for_debugger(); + ResultMsg::DebugOutput(DebugOutput::FetchDocuments(json)) + } + _ => { + ResultMsg::DebugCommand(option) + } + }; self.result_tx.send(msg).unwrap(); let notifier = self.notifier.lock(); notifier.unwrap() @@ -514,4 +534,139 @@ impl RenderBackend { let mut notifier = self.notifier.lock(); notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed); } + + + #[cfg(not(feature = "debugger"))] + fn get_docs_for_debugger(&self) -> String { + String::new() + } + + #[cfg(feature = "debugger")] + fn traverse_items<'a>(&self, + traversal: &mut BuiltDisplayListIter<'a>, + node: &mut debug_server::TreeNode) { + loop { + let subtraversal = { + let item = match traversal.next() { + Some(item) => item, + None => break, + }; + + match *item.item() { + display_item @ SpecificDisplayItem::PushStackingContext(..) => { + let mut subtraversal = item.sub_iter(); + let mut child_node = debug_server::TreeNode::new(&display_item.debug_string()); + self.traverse_items(&mut subtraversal, &mut child_node); + node.add_child(child_node); + Some(subtraversal) + } + SpecificDisplayItem::PopStackingContext => { + return; + } + display_item => { + node.add_item(&display_item.debug_string()); + None + } + } + }; + + // If flatten_item created a sub-traversal, we need `traversal` to have the + // same state as the completed subtraversal, so we reinitialize it here. + if let Some(subtraversal) = subtraversal { + *traversal = subtraversal; + } + } + } + + #[cfg(feature = "debugger")] + fn get_docs_for_debugger(&self) -> String { + let mut docs = debug_server::DocumentList::new(); + + for (_, doc) in &self.documents { + let mut debug_doc = debug_server::TreeNode::new("document"); + + for (_, display_list) in &doc.scene.display_lists { + let mut debug_dl = debug_server::TreeNode::new("display_list"); + self.traverse_items(&mut display_list.iter(), &mut debug_dl); + debug_doc.add_child(debug_dl); + } + + docs.add(debug_doc); + } + + serde_json::to_string(&docs).unwrap() + } +} + +#[cfg(feature = "debugger")] +trait ToDebugString { + fn debug_string(&self) -> String; +} + +#[cfg(feature = "debugger")] +impl ToDebugString for SpecificDisplayItem { + fn debug_string(&self) -> String { + match *self { + SpecificDisplayItem::Image(..) => { + String::from("image") + } + SpecificDisplayItem::YuvImage(..) => { + String::from("yuv_image") + } + SpecificDisplayItem::Text(..) => { + String::from("text") + } + SpecificDisplayItem::Rectangle(..) => { + String::from("rectangle") + } + SpecificDisplayItem::Line(..) => { + String::from("line") + } + SpecificDisplayItem::Gradient(..) => { + String::from("gradient") + } + SpecificDisplayItem::RadialGradient(..) => { + String::from("radial_gradient") + } + SpecificDisplayItem::BoxShadow(..) => { + String::from("box_shadow") + } + SpecificDisplayItem::Border(..) => { + String::from("border") + } + SpecificDisplayItem::PushStackingContext(..) => { + String::from("push_stacking_context") + } + SpecificDisplayItem::Iframe(..) => { + String::from("iframe") + } + SpecificDisplayItem::Clip(..) => { + String::from("clip") + } + SpecificDisplayItem::ScrollFrame(..) => { + String::from("scroll_frame") + } + SpecificDisplayItem::StickyFrame(..) => { + String::from("sticky_frame") + } + SpecificDisplayItem::PushNestedDisplayList => { + String::from("push_nested_display_list") + } + SpecificDisplayItem::PopNestedDisplayList => { + String::from("pop_nested_display_list") + } + SpecificDisplayItem::SetGradientStops => { + String::from("set_gradient_stops") + } + SpecificDisplayItem::PopStackingContext => { + String::from("pop_stacking_context") + } + SpecificDisplayItem::PushTextShadow(..) => { + String::from("push_text_shadow") + } + SpecificDisplayItem::PopTextShadow => { + String::from("pop_text_shadow") + } + } + } } diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index 6379df3573fb..bbd2d4a6bf58 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -166,6 +166,7 @@ pub struct RenderTaskData { pub enum RenderTaskKind { Alpha(AlphaRenderTask), CachePrimitive(PrimitiveIndex), + BoxShadow(PrimitiveIndex), CacheMask(CacheMaskTask), VerticalBlur(DeviceIntLength), HorizontalBlur(DeviceIntLength), @@ -217,7 +218,7 @@ impl RenderTask { cache_key: Some(RenderTaskKey::BoxShadow(key)), children: Vec::new(), location: RenderTaskLocation::Dynamic(None, size), - kind: RenderTaskKind::CachePrimitive(prim_index), + kind: RenderTaskKind::BoxShadow(prim_index), } } @@ -344,6 +345,7 @@ impl RenderTask { match self.kind { RenderTaskKind::Alpha(ref mut task) => task, RenderTaskKind::CachePrimitive(..) | + RenderTaskKind::BoxShadow(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | @@ -356,6 +358,7 @@ impl RenderTask { match self.kind { RenderTaskKind::Alpha(ref task) => task, RenderTaskKind::CachePrimitive(..) | + RenderTaskKind::BoxShadow(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | @@ -396,7 +399,8 @@ impl RenderTask { ], } } - RenderTaskKind::CachePrimitive(..) => { + RenderTaskKind::CachePrimitive(..) | + RenderTaskKind::BoxShadow(..) => { let (target_rect, target_index) = self.get_target_rect(); RenderTaskData { data: [ @@ -500,7 +504,10 @@ impl RenderTask { RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | RenderTaskKind::HorizontalBlur(..) => RenderTargetKind::Color, - RenderTaskKind::CacheMask(..) => RenderTargetKind::Alpha, + + RenderTaskKind::CacheMask(..) | + RenderTaskKind::BoxShadow(..) => RenderTargetKind::Alpha, + RenderTaskKind::Alias(..) => { panic!("BUG: target_kind() called on invalidated task"); } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 5f251a646f5c..600252fe4c49 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -17,7 +17,7 @@ use api::DebugCommand; use debug_colors; use debug_render::DebugRenderer; #[cfg(feature = "debugger")] -use debug_server::{self, DebugMsg, DebugServer}; +use debug_server::{self, DebugServer}; use device::{DepthFunction, Device, FrameId, Program, Texture, VertexDescriptor, GpuMarker, GpuProfiler, PBOId}; use device::{GpuTimer, TextureFilter, VAO, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError}; use device::{ExternalTexture, get_gl_format_bgra, TextureSlot, VertexAttribute, VertexAttributeKind}; @@ -26,7 +26,7 @@ use frame_builder::FrameBuilderConfig; use gleam::gl; use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; use internal_types::{FastHashMap, CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp}; -use internal_types::{TextureUpdateList, RenderTargetMode, TextureUpdateSource}; +use internal_types::{DebugOutput, TextureUpdateList, RenderTargetMode, TextureUpdateSource}; use internal_types::{BatchTextures, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture}; use profiler::{Profiler, BackendProfileCounters}; use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters}; @@ -1587,6 +1587,13 @@ impl Renderer { ResultMsg::RefreshShader(path) => { self.pending_shader_updates.push(path); } + ResultMsg::DebugOutput(output) => { + match output { + DebugOutput::FetchDocuments(string) => { + self.debug_server.send(string); + } + } + } ResultMsg::DebugCommand(command) => { self.handle_debug_command(command); } @@ -1595,72 +1602,66 @@ impl Renderer { } #[cfg(not(feature = "debugger"))] - fn update_debug_server(&self) { + fn get_passes_for_debugger(&self) -> String { // Avoid unused param warning. let _ = &self.debug_server; + String::new() } #[cfg(feature = "debugger")] - fn update_debug_server(&self) { - while let Ok(msg) = self.debug_server.debug_rx.try_recv() { - match msg { - DebugMsg::FetchPasses(sender) => { - let mut debug_passes = debug_server::PassList::new(); + fn get_passes_for_debugger(&self) -> String { + let mut debug_passes = debug_server::PassList::new(); - if let Some(frame) = self.current_frame.as_ref().and_then(|frame| frame.frame.as_ref()) { - for pass in &frame.passes { - let mut debug_pass = debug_server::Pass::new(); + if let Some(frame) = self.current_frame.as_ref().and_then(|frame| frame.frame.as_ref()) { + for pass in &frame.passes { + let mut debug_pass = debug_server::Pass::new(); - for target in &pass.alpha_targets.targets { - let mut debug_target = debug_server::Target::new("A8"); + for target in &pass.alpha_targets.targets { + let mut debug_target = debug_server::Target::new("A8"); - debug_target.add(debug_server::BatchKind::Clip, "Clear", target.clip_batcher.border_clears.len()); - debug_target.add(debug_server::BatchKind::Clip, "Borders", target.clip_batcher.borders.len()); - debug_target.add(debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len()); - for (_, items) in target.clip_batcher.images.iter() { - debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len()); - } + debug_target.add(debug_server::BatchKind::Clip, "Clear", target.clip_batcher.border_clears.len()); + debug_target.add(debug_server::BatchKind::Clip, "Borders", target.clip_batcher.borders.len()); + debug_target.add(debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len()); + for (_, items) in target.clip_batcher.images.iter() { + debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len()); + } + debug_target.add(debug_server::BatchKind::Cache, "Box Shadow", target.box_shadow_cache_prims.len()); - debug_pass.add(debug_target); - } + debug_pass.add(debug_target); + } - for target in &pass.color_targets.targets { - let mut debug_target = debug_server::Target::new("RGBA8"); + for target in &pass.color_targets.targets { + let mut debug_target = debug_server::Target::new("RGBA8"); - debug_target.add(debug_server::BatchKind::Cache, "Vertical Blur", target.vertical_blurs.len()); - debug_target.add(debug_server::BatchKind::Cache, "Horizontal Blur", target.horizontal_blurs.len()); - debug_target.add(debug_server::BatchKind::Cache, "Box Shadow", target.box_shadow_cache_prims.len()); - debug_target.add(debug_server::BatchKind::Cache, "Text Shadow", target.text_run_cache_prims.len()); - debug_target.add(debug_server::BatchKind::Cache, "Lines", target.line_cache_prims.len()); + debug_target.add(debug_server::BatchKind::Cache, "Vertical Blur", target.vertical_blurs.len()); + debug_target.add(debug_server::BatchKind::Cache, "Horizontal Blur", target.horizontal_blurs.len()); + debug_target.add(debug_server::BatchKind::Cache, "Text Shadow", target.text_run_cache_prims.len()); + debug_target.add(debug_server::BatchKind::Cache, "Lines", target.line_cache_prims.len()); - for batch in target.alpha_batcher - .batch_list - .opaque_batch_list - .batches - .iter() - .rev() { - debug_target.add(debug_server::BatchKind::Opaque, batch.key.kind.debug_name(), batch.instances.len()); - } - - for batch in &target.alpha_batcher - .batch_list - .alpha_batch_list - .batches { - debug_target.add(debug_server::BatchKind::Alpha, batch.key.kind.debug_name(), batch.instances.len()); - } - - debug_pass.add(debug_target); - } - - debug_passes.add(debug_pass); - } + for batch in target.alpha_batcher + .batch_list + .opaque_batch_list + .batches + .iter() + .rev() { + debug_target.add(debug_server::BatchKind::Opaque, batch.key.kind.debug_name(), batch.instances.len()); } - let json = serde_json::to_string(&debug_passes).unwrap(); - sender.send(json).ok(); + for batch in &target.alpha_batcher + .batch_list + .alpha_batch_list + .batches { + debug_target.add(debug_server::BatchKind::Alpha, batch.key.kind.debug_name(), batch.instances.len()); + } + + debug_pass.add(debug_target); } + + debug_passes.add(debug_pass); } } + + serde_json::to_string(&debug_passes).unwrap() } fn handle_debug_command(&mut self, command: DebugCommand) { @@ -1686,8 +1687,10 @@ impl Renderer { self.debug_flags.remove(RENDER_TARGET_DBG); } } - DebugCommand::Flush => { - self.update_debug_server(); + DebugCommand::FetchDocuments => {} + DebugCommand::FetchPasses => { + let json = self.get_passes_for_debugger(); + self.debug_server.send(json); } } } @@ -1839,7 +1842,6 @@ impl Renderer { // Ensure no PBO is bound when creating the texture storage, // or GL will attempt to read data from there. - self.device.bind_pbo(None); self.device.init_texture(texture, width, height, @@ -1882,6 +1884,9 @@ impl Renderer { layer_index, stride, 0); + + // Ensure that other texture updates won't read from this PBO. + self.device.bind_pbo(None); } TextureUpdateOp::Free => { let texture = &mut self.texture_resolver.cache_texture_map[update.id.0]; @@ -1890,9 +1895,6 @@ impl Renderer { } } } - - // Ensure that other texture updates won't read from this PBO. - self.device.bind_pbo(None); } fn draw_instanced_batch(&mut self, @@ -2164,16 +2166,6 @@ impl Renderer { } } - // Draw any box-shadow caches for this target. - if !target.box_shadow_cache_prims.is_empty() { - self.device.set_blend(false); - let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW); - self.cs_box_shadow.bind(&mut self.device, projection); - self.draw_instanced_batch(&target.box_shadow_cache_prims, - VertexArrayKind::CacheBoxShadow, - &BatchTextures::no_texture()); - } - // Draw any textrun caches for this target. For now, this // is only used to cache text runs that are to be blurred // for text-shadow support. In the future it may be worth @@ -2297,6 +2289,16 @@ impl Renderer { target.used_rect()); } + // Draw any box-shadow caches for this target. + if !target.box_shadow_cache_prims.is_empty() { + self.device.set_blend(false); + let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW); + self.cs_box_shadow.bind(&mut self.device, projection); + self.draw_instanced_batch(&target.box_shadow_cache_prims, + VertexArrayKind::CacheBoxShadow, + &BatchTextures::no_texture()); + } + // Draw the clip items into the tiled alpha mask. { let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP); @@ -2863,4 +2865,6 @@ impl DebugServer { pub fn new(_: MsgSender) -> DebugServer { DebugServer } + + pub fn send(&mut self, _: String) {} } diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index ef7b84ae26d6..466f0427833b 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use app_units::Au; use device::TextureFilter; use frame::FrameId; use glyph_cache::GlyphCache; @@ -17,7 +18,9 @@ use texture_cache::{TextureCache, TextureCacheHandle}; use api::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest}; use api::{BlobImageResources, BlobImageData, ResourceUpdates, ResourceUpdate, AddFont}; use api::{DevicePoint, DeviceUintRect, DeviceUintSize}; -use api::{Epoch, FontInstance, FontKey, FontTemplate}; +use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate}; +use api::{FontInstanceOptions, FontInstancePlatformOptions}; +use api::{ColorF, FontRenderMode, SubpixelDirection}; use api::{GlyphDimensions, GlyphKey, IdNamespace}; use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering}; use api::{TileOffset, TileSize}; @@ -170,6 +173,7 @@ impl Into for ImageRequest { struct Resources { font_templates: FastHashMap, + font_instances: FastHashMap, image_templates: ImageTemplates, } @@ -213,6 +217,7 @@ impl ResourceCache { cached_images: ResourceClassCache::new(), resources: Resources { font_templates: FastHashMap::default(), + font_instances: FastHashMap::default(), image_templates: ImageTemplates::new(), }, cached_glyph_dimensions: FastHashMap::default(), @@ -279,6 +284,13 @@ impl ResourceCache { ResourceUpdate::DeleteFont(font) => { self.delete_font_template(font); } + ResourceUpdate::AddFontInstance(instance) => { + self.add_font_instance(instance.key, instance.font_key, instance.glyph_size, + instance.options, instance.platform_options); + } + ResourceUpdate::DeleteFontInstance(instance) => { + self.delete_font_instance(instance); + } } } } @@ -298,6 +310,40 @@ impl ResourceCache { } } + pub fn add_font_instance(&mut self, + instance_key: FontInstanceKey, + font_key: FontKey, + glyph_size: Au, + options: Option, + platform_options: Option) { + let mut render_mode = FontRenderMode::Subpixel; + let mut subpx_dir = SubpixelDirection::Horizontal; + if let Some(options) = options { + render_mode = options.render_mode; + if render_mode == FontRenderMode::Mono { + subpx_dir = SubpixelDirection::None; + } + } + let instance = FontInstance::new(font_key, + glyph_size, + ColorF::new(0.0, 0.0, 0.0, 1.0), + render_mode, + subpx_dir, + platform_options); + self.resources.font_instances.insert(instance_key, instance); + } + + pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) { + self.resources.font_instances.remove(&instance_key); + if let Some(ref mut r) = self.blob_image_renderer { + r.delete_font_instance(instance_key); + } + } + + pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<&FontInstance> { + self.resources.font_instances.get(&instance_key) + } + pub fn add_image_template(&mut self, image_key: ImageKey, descriptor: ImageDescriptor, @@ -607,13 +653,11 @@ impl ResourceCache { ); // Apply any updates of new / updated images (incl. blobs) to the texture cache. - self.update_texture_cache(gpu_cache, texture_cache_profile); - self.texture_cache.end_frame(); + self.update_texture_cache(gpu_cache); + self.texture_cache.end_frame(texture_cache_profile); } - fn update_texture_cache(&mut self, - gpu_cache: &mut GpuCache, - _texture_cache_profile: &mut TextureCacheProfileCounters) { + fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) { for request in self.pending_image_requests.drain() { let image_template = self.resources.image_templates.get_mut(request.key).unwrap(); debug_assert!(image_template.data.uses_texture_cache()); @@ -667,7 +711,7 @@ impl ResourceCache { let (stride, offset) = if tiled_on_cpu { (image_descriptor.stride, 0) } else { - let bpp = image_descriptor.format.bytes_per_pixel().unwrap(); + let bpp = image_descriptor.format.bytes_per_pixel(); let stride = image_descriptor.compute_stride(); let offset = image_descriptor.offset + tile.y as u32 * tile_size as u32 * stride + tile.x as u32 * tile_size as u32 * bpp; diff --git a/gfx/webrender/src/scene.rs b/gfx/webrender/src/scene.rs index ea7a7f28a13c..9e7a5fb3e3bf 100644 --- a/gfx/webrender/src/scene.rs +++ b/gfx/webrender/src/scene.rs @@ -127,4 +127,13 @@ impl Scene { self.pipeline_map.insert(pipeline_id, new_pipeline); } + + pub fn remove_pipeline(&mut self, + pipeline_id: PipelineId) { + if self.root_pipeline_id == Some(pipeline_id) { + self.root_pipeline_id = None; + } + self.display_lists.remove(&pipeline_id); + self.pipeline_map.remove(&pipeline_id); + } } diff --git a/gfx/webrender/src/texture_cache.rs b/gfx/webrender/src/texture_cache.rs index 4b6f3e3b8230..a204f41f109b 100644 --- a/gfx/webrender/src/texture_cache.rs +++ b/gfx/webrender/src/texture_cache.rs @@ -8,6 +8,7 @@ use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle}; use gpu_cache::{GpuCache, GpuCacheHandle}; use internal_types::{SourceTexture, TextureUpdate, TextureUpdateOp}; use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList, TextureUpdateSource}; +use profiler::{ResourceProfileCounter, TextureCacheProfileCounters}; use resource_cache::CacheItem; use std::cmp; use std::mem; @@ -223,8 +224,13 @@ impl TextureCache { self.frame_id = frame_id; } - pub fn end_frame(&mut self) { + pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) { self.expire_old_standalone_entries(); + + self.array_a8.update_profile(&mut texture_cache_profile.pages_a8); + self.array_rg8.update_profile(&mut texture_cache_profile.pages_rg8); + self.array_rgb8.update_profile(&mut texture_cache_profile.pages_rgb8); + self.array_rgba8.update_profile(&mut texture_cache_profile.pages_rgba8); } // Request an item in the texture cache. All images that will @@ -835,6 +841,18 @@ impl TextureArray { } } + fn update_profile(&self, counter: &mut ResourceProfileCounter) { + if self.is_allocated { + let size = TEXTURE_ARRAY_LAYERS as u32 * + TEXTURE_LAYER_DIMENSIONS * + TEXTURE_LAYER_DIMENSIONS * + self.format.bytes_per_pixel(); + counter.set(TEXTURE_ARRAY_LAYERS as usize, size as usize); + } else { + counter.set(0, 0); + } + } + // Allocate space in this texture array. fn alloc(&mut self, width: u32, @@ -957,7 +975,7 @@ impl TextureUpdate { } ImageData::Raw(bytes) => { let finish = descriptor.offset + - descriptor.width * descriptor.format.bytes_per_pixel().unwrap_or(0) + + descriptor.width * descriptor.format.bytes_per_pixel() + (descriptor.height-1) * descriptor.compute_stride(); assert!(bytes.len() >= finish as usize); diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index f207ea61a4c0..5daa11623ae8 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -22,7 +22,7 @@ use std::{f32, i32, usize}; use texture_allocator::GuillotineAllocator; use util::{TransformedRect, TransformedRectKind}; use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey}; -use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize, FontInstance}; +use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize}; use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect}; use api::{LayerToWorldTransform, MixBlendMode, PipelineId, PropertyBinding, TransformStyle}; use api::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D}; @@ -44,7 +44,7 @@ impl AlphaBatchHelpers for PrimitiveStore { match metadata.prim_kind { PrimitiveKind::TextRun => { let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0]; - match text_run_cpu.normal_render_mode { + match text_run_cpu.font.render_mode { FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.color), FontRenderMode::Alpha | FontRenderMode::Mono => BlendMode::Alpha, } @@ -453,17 +453,12 @@ impl AlphaRenderItem { } PrimitiveKind::TextRun => { let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0]; - let font_size_dp = text_cpu.logical_font_size.scale_by(ctx.device_pixel_ratio); // TODO(gw): avoid / recycle this allocation in the future. let mut instances = Vec::new(); - let font = FontInstance::new(text_cpu.font_key, - font_size_dp, - text_cpu.color, - text_cpu.normal_render_mode, - text_cpu.glyph_options, - text_cpu.subpx_dir); + let mut font = text_cpu.font.clone(); + font.size = font.size.scale_by(ctx.device_pixel_ratio); let texture_id = ctx.resource_cache.get_glyphs(font, &text_cpu.glyph_keys, @@ -576,8 +571,9 @@ impl AlphaRenderItem { let box_shadow = &ctx.prim_store.cpu_box_shadows[prim_metadata.cpu_prim_index.0]; let cache_task_id = prim_metadata.render_task_id.unwrap(); let cache_task_address = render_tasks.get_task_address(cache_task_id); + let textures = BatchTextures::render_target_cache(); - let key = AlphaBatchKey::new(AlphaBatchKind::BoxShadow, flags, blend_mode, no_textures); + let key = AlphaBatchKey::new(AlphaBatchKind::BoxShadow, flags, blend_mode, textures); let batch = batch_list.get_suitable_batch(&key, item_bounding_rect); for rect_index in 0..box_shadow.rects.len() { @@ -906,7 +902,6 @@ impl RenderTargetList { /// A render target represents a number of rendering operations on a surface. pub struct ColorRenderTarget { pub alpha_batcher: AlphaBatcher, - pub box_shadow_cache_prims: Vec, // List of text runs to be cached to this render target. // TODO(gw): For now, assume that these all come from // the same source texture id. This is almost @@ -933,7 +928,6 @@ impl RenderTarget for ColorRenderTarget { fn new(size: DeviceUintSize) -> ColorRenderTarget { ColorRenderTarget { alpha_batcher: AlphaBatcher::new(), - box_shadow_cache_prims: Vec::new(), text_run_cache_prims: Vec::new(), line_cache_prims: Vec::new(), text_run_textures: BatchTextures::no_texture(), @@ -993,16 +987,9 @@ impl RenderTarget for ColorRenderTarget { } RenderTaskKind::CachePrimitive(prim_index) => { let prim_metadata = ctx.prim_store.get_metadata(prim_index); - let prim_address = prim_metadata.gpu_location.as_int(gpu_cache); match prim_metadata.prim_kind { - PrimitiveKind::BoxShadow => { - self.box_shadow_cache_prims.push(BoxShadowCacheInstance { - prim_address: gpu_cache.get_address(&prim_metadata.gpu_location), - task_index: render_tasks.get_task_address(task_id), - }); - } PrimitiveKind::TextShadow => { let prim = &ctx.prim_store.cpu_text_shadows[prim_metadata.cpu_prim_index.0]; @@ -1026,14 +1013,9 @@ impl RenderTarget for ColorRenderTarget { // the parent text-shadow prim address as a user data field, allowing // the shader to fetch the text-shadow parameters. let text = &ctx.prim_store.cpu_text_runs[sub_metadata.cpu_prim_index.0]; - let font_size_dp = text.logical_font_size.scale_by(ctx.device_pixel_ratio); - let font = FontInstance::new(text.font_key, - font_size_dp, - text.color, - text.shadow_render_mode, - text.glyph_options, - text.subpx_dir); + let mut font = text.font.clone(); + font.size = font.size.scale_by(ctx.device_pixel_ratio); let texture_id = ctx.resource_cache.get_glyphs(font, &text.glyph_keys, @@ -1073,7 +1055,8 @@ impl RenderTarget for ColorRenderTarget { } } } - RenderTaskKind::CacheMask(..) => { + RenderTaskKind::CacheMask(..) | + RenderTaskKind::BoxShadow(..) => { panic!("Should not be added to color target!"); } RenderTaskKind::Readback(device_rect) => { @@ -1085,6 +1068,7 @@ impl RenderTarget for ColorRenderTarget { pub struct AlphaRenderTarget { pub clip_batcher: ClipBatcher, + pub box_shadow_cache_prims: Vec, allocator: TextureAllocator, } @@ -1096,6 +1080,7 @@ impl RenderTarget for AlphaRenderTarget { fn new(size: DeviceUintSize) -> AlphaRenderTarget { AlphaRenderTarget { clip_batcher: ClipBatcher::new(), + box_shadow_cache_prims: Vec::new(), allocator: TextureAllocator::new(size), } } @@ -1121,6 +1106,21 @@ impl RenderTarget for AlphaRenderTarget { RenderTaskKind::Readback(..) => { panic!("Should not be added to alpha target!"); } + RenderTaskKind::BoxShadow(prim_index) => { + let prim_metadata = ctx.prim_store.get_metadata(prim_index); + + match prim_metadata.prim_kind { + PrimitiveKind::BoxShadow => { + self.box_shadow_cache_prims.push(BoxShadowCacheInstance { + prim_address: gpu_cache.get_address(&prim_metadata.gpu_location), + task_index: render_tasks.get_task_address(task_id), + }); + } + _ => { + panic!("BUG: invalid prim kind"); + } + } + } RenderTaskKind::CacheMask(ref task_info) => { let task_address = render_tasks.get_task_address(task_id); self.clip_batcher.add(task_address, diff --git a/gfx/webrender_api/Cargo.toml b/gfx/webrender_api/Cargo.toml index 42b2cd2aa98f..3222bdc68507 100644 --- a/gfx/webrender_api/Cargo.toml +++ b/gfx/webrender_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender_api" -version = "0.49.0" +version = "0.50.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index 2a03faf95411..ff5c2c56f41c 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -2,14 +2,15 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use app_units::Au; use channel::{self, MsgSender, Payload, PayloadSenderHelperMethods, PayloadSender}; use std::cell::Cell; use std::fmt; use std::marker::PhantomData; use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint}; -use {DeviceUintRect, DeviceUintSize, FontKey, GlyphDimensions, GlyphKey}; +use {DeviceUintRect, DeviceUintSize, FontInstanceKey, FontKey, GlyphDimensions, GlyphKey}; use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutVector2D, LayoutSize, LayoutTransform}; -use {FontInstance, NativeFontHandle, WorldPoint}; +use {FontInstance, FontInstanceOptions, FontInstancePlatformOptions, NativeFontHandle, WorldPoint}; pub type TileSize = u16; @@ -26,6 +27,8 @@ pub enum ResourceUpdate { DeleteImage(ImageKey), AddFont(AddFont), DeleteFont(FontKey), + AddFontInstance(AddFontInstance), + DeleteFontInstance(FontInstanceKey), } impl ResourceUpdates { @@ -71,6 +74,19 @@ impl ResourceUpdates { self.updates.push(ResourceUpdate::DeleteFont(key)); } + pub fn add_font_instance(&mut self, + key: FontInstanceKey, + font_key: FontKey, + glyph_size: Au, + options: Option, + platform_options: Option) { + self.updates.push(ResourceUpdate::AddFontInstance(AddFontInstance { key, font_key, glyph_size, options, platform_options })); + } + + pub fn delete_font_instance(&mut self, key: FontInstanceKey) { + self.updates.push(ResourceUpdate::DeleteFontInstance(key)); + } + pub fn merge(&mut self, mut other: ResourceUpdates) { self.updates.append(&mut other.updates); } @@ -102,6 +118,15 @@ pub enum AddFont { Native(FontKey, NativeFontHandle), } +#[derive(Clone, Deserialize, Serialize)] +pub struct AddFontInstance { + pub key: FontInstanceKey, + pub font_key: FontKey, + pub glyph_size: Au, + pub options: Option, + pub platform_options: Option, +} + #[derive(Clone, Deserialize, Serialize)] pub enum DocumentMsg { SetDisplayList { @@ -118,6 +143,7 @@ pub enum DocumentMsg { SetPinchZoom(ZoomFactor), SetPan(DeviceIntPoint), SetRootPipeline(PipelineId), + RemovePipeline(PipelineId), SetWindowParameters { window_size: DeviceUintSize, inner_rect: DeviceUintRect, @@ -137,6 +163,7 @@ impl fmt::Debug for DocumentMsg { DocumentMsg::SetPinchZoom(..) => "DocumentMsg::SetPinchZoom", DocumentMsg::SetPan(..) => "DocumentMsg::SetPan", DocumentMsg::SetRootPipeline(..) => "DocumentMsg::SetRootPipeline", + DocumentMsg::RemovePipeline(..) => "DocumentMsg::RemovePipeline", DocumentMsg::SetWindowParameters{..} => "DocumentMsg::SetWindowParameters", DocumentMsg::Scroll(..) => "DocumentMsg::Scroll", DocumentMsg::ScrollNodeWithId(..) => "DocumentMsg::ScrollNodeWithId", @@ -155,8 +182,10 @@ pub enum DebugCommand { EnableTextureCacheDebug(bool), // Display intermediate render targets on screen. EnableRenderTargetDebug(bool), - // Flush any pending debug commands. - Flush, + // Fetch current documents and display lists. + FetchDocuments, + // Fetch current passes and batches. + FetchPasses, } #[derive(Clone, Deserialize, Serialize)] @@ -329,6 +358,11 @@ impl RenderApi { FontKey::new(self.namespace_id, new_id) } + pub fn generate_font_instance_key(&self) -> FontInstanceKey { + let new_id = self.next_unique_id(); + FontInstanceKey::new(self.namespace_id, new_id) + } + /// Gets the dimensions for the supplied glyph keys /// /// Note: Internally, the internal texture cache doesn't store @@ -422,7 +456,7 @@ impl RenderApi { self.api_sender.send(ApiMsg::UpdateDocument(document_id, msg)).unwrap() } - /// Sets the root pipeline. + /// Sets the root pipeline. /// /// # Examples /// @@ -439,6 +473,13 @@ impl RenderApi { self.send(document_id, DocumentMsg::SetRootPipeline(pipeline_id)); } + /// Removes data associated with a pipeline from the internal data structures. + /// If the specified `pipeline_id` is for the root pipeline, the root pipeline + /// is reset back to `None`. + pub fn remove_pipeline(&self, document_id: DocumentId, pipeline_id: PipelineId) { + self.send(document_id, DocumentMsg::RemovePipeline(pipeline_id)); + } + /// Supplies a new frame to WebRender. /// /// Non-blocking, it notifies a worker process which processes the display list. diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index 61be7b101b1a..ac5282421fbb 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -2,9 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use app_units::Au; use euclid::{SideOffsets2D, TypedSideOffsets2D}; -use {ColorF, FontKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform}; +use {ColorF, FontInstanceKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform}; use {GlyphOptions, LayoutVector2D, PipelineId, PropertyBinding}; // NOTE: some of these structs have an "IMPLICIT" comment. @@ -139,8 +138,7 @@ pub enum LineStyle { #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct TextDisplayItem { - pub font_key: FontKey, - pub size: Au, + pub font_key: FontInstanceKey, pub color: ColorF, pub glyph_options: Option, } // IMPLICIT: glyphs: Vec diff --git a/gfx/webrender_api/src/display_list.rs b/gfx/webrender_api/src/display_list.rs index 6e3b113a61ca..36d53f25dd34 100644 --- a/gfx/webrender_api/src/display_list.rs +++ b/gfx/webrender_api/src/display_list.rs @@ -2,14 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use app_units::Au; use bincode; use serde::{Deserialize, Serialize, Serializer}; use serde::ser::{SerializeSeq, SerializeMap}; use time::precise_time_ns; use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem}; use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion, DisplayItem}; -use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontKey, GlyphIndex, GlyphInstance}; +use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontInstanceKey, GlyphIndex, GlyphInstance}; use {GlyphOptions, Gradient, GradientDisplayItem, GradientStop, IframeDisplayItem}; use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, LayoutPoint, LayoutRect, LayoutSize}; use {LayoutTransform, LayoutVector2D, LineDisplayItem, LineOrientation, LineStyle, LocalClip}; @@ -301,7 +300,7 @@ impl<'a> BuiltDisplayListIter<'a> { } impl<'a> Iterator for GlyphsIter<'a> { - type Item = (FontKey, ColorF, ItemRange); + type Item = (FontInstanceKey, ColorF, ItemRange); fn next(&mut self) -> Option { if self.data.len() == 0 { return None; } @@ -457,7 +456,7 @@ pub struct DisplayListBuilder { pub pipeline_id: PipelineId, clip_stack: Vec, // FIXME: audit whether fast hashers (FNV?) are safe here - glyphs: FastHashMap<(FontKey, ColorF), FastHashSet>, + glyphs: FastHashMap<(FontInstanceKey, ColorF), FastHashSet>, next_clip_id: u64, builder_start_time: u64, @@ -605,38 +604,28 @@ impl DisplayListBuilder { rect: LayoutRect, local_clip: Option, glyphs: &[GlyphInstance], - font_key: FontKey, + font_key: FontInstanceKey, color: ColorF, - size: Au, glyph_options: Option) { - // Sanity check - anything with glyphs bigger than this - // is probably going to consume too much memory to render - // efficiently anyway. This is specifically to work around - // the font_advance.html reftest, which creates a very large - // font as a crash test - the rendering is also ignored - // by the azure renderer. - if size < Au::from_px(4096) { - let item = SpecificDisplayItem::Text(TextDisplayItem { - color, - font_key, - size, - glyph_options, - }); + let item = SpecificDisplayItem::Text(TextDisplayItem { + color, + font_key, + glyph_options, + }); - for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) { - self.push_item(item, rect, local_clip); - self.push_iter(split_glyphs); + for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) { + self.push_item(item, rect, local_clip); + self.push_iter(split_glyphs); - // Remember that we've seen these glyphs - self.cache_glyphs(font_key, color, split_glyphs.iter().map(|glyph| glyph.index)); - } + // Remember that we've seen these glyphs + self.cache_glyphs(font_key, color, split_glyphs.iter().map(|glyph| glyph.index)); } } fn cache_glyphs>(&mut self, - font_key: FontKey, - color: ColorF, - glyphs: I) { + font_key: FontInstanceKey, + color: ColorF, + glyphs: I) { let mut font_glyphs = self.glyphs.entry((font_key, color)) .or_insert(FastHashSet::default()); diff --git a/gfx/webrender_api/src/font.rs b/gfx/webrender_api/src/font.rs index 41cb6ed67355..0e6689182c7f 100644 --- a/gfx/webrender_api/src/font.rs +++ b/gfx/webrender_api/src/font.rs @@ -72,7 +72,7 @@ pub enum FontTemplate { Native(NativeFontHandle), } -#[repr(C)] +#[repr(u32)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)] pub enum FontRenderMode { Mono = 0, @@ -80,7 +80,7 @@ pub enum FontRenderMode { Subpixel, } -#[repr(C)] +#[repr(u32)] #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] pub enum SubpixelDirection { None = 0, @@ -115,6 +115,14 @@ impl FontRenderMode { _ => panic!("Should only be given the fractional part"), } } + + // Combine two font render modes such that the lesser amount of AA limits the AA of the result. + pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode { + match (self, other) { + (FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other, + _ => self, + } + } } #[repr(u8)] @@ -137,8 +145,21 @@ impl Into for SubpixelOffset { } } +#[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct GlyphOptions { + pub render_mode: FontRenderMode, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] +pub struct FontInstanceOptions { + pub render_mode: FontRenderMode, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] +pub struct FontInstancePlatformOptions { // These are currently only used on windows for dwrite fonts. pub use_embedded_bitmap: bool, pub force_gdi_rendering: bool, @@ -155,8 +176,8 @@ pub struct FontInstance { pub size: Au, pub color: ColorU, pub render_mode: FontRenderMode, - pub glyph_options: Option, pub subpx_dir: SubpixelDirection, + pub platform_options: Option, } impl FontInstance { @@ -164,8 +185,8 @@ impl FontInstance { size: Au, mut color: ColorF, render_mode: FontRenderMode, - glyph_options: Option, - subpx_dir: SubpixelDirection) -> FontInstance { + subpx_dir: SubpixelDirection, + platform_options: Option) -> FontInstance { // In alpha/mono mode, the color of the font is irrelevant. // Forcing it to black in those cases saves rasterizing glyphs // of different colors when not needed. @@ -178,8 +199,8 @@ impl FontInstance { size, color: color.into(), render_mode, - glyph_options, subpx_dir, + platform_options, } } @@ -192,6 +213,16 @@ impl FontInstance { } } +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)] +pub struct FontInstanceKey(pub IdNamespace, pub u32); + +impl FontInstanceKey { + pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey { + FontInstanceKey(namespace, key) + } +} + #[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] pub struct GlyphKey { pub index: u32, diff --git a/gfx/webrender_api/src/image.rs b/gfx/webrender_api/src/image.rs index 9b5fd755aeda..f489d8f8c4cd 100644 --- a/gfx/webrender_api/src/image.rs +++ b/gfx/webrender_api/src/image.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use {DeviceUintRect, DevicePoint}; use {IdNamespace}; use {TileOffset, TileSize}; -use font::{FontKey, FontTemplate}; +use font::{FontKey, FontInstanceKey, FontTemplate}; #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -59,14 +59,14 @@ pub enum ImageFormat { } impl ImageFormat { - pub fn bytes_per_pixel(self) -> Option { + pub fn bytes_per_pixel(self) -> u32 { match self { - ImageFormat::A8 => Some(1), - ImageFormat::RGB8 => Some(3), - ImageFormat::BGRA8 => Some(4), - ImageFormat::RGBAF32 => Some(16), - ImageFormat::RG8 => Some(2), - ImageFormat::Invalid => None, + ImageFormat::A8 => 1, + ImageFormat::RGB8 => 3, + ImageFormat::BGRA8 => 4, + ImageFormat::RGBAF32 => 16, + ImageFormat::RG8 => 2, + ImageFormat::Invalid => 0, } } } @@ -94,7 +94,7 @@ impl ImageDescriptor { } pub fn compute_stride(&self) -> u32 { - self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap()) + self.stride.unwrap_or(self.width * self.format.bytes_per_pixel()) } } @@ -165,6 +165,8 @@ pub trait BlobImageRenderer: Send { fn resolve(&mut self, key: BlobImageRequest) -> BlobImageResult; fn delete_font(&mut self, key: FontKey); + + fn delete_font_instance(&mut self, key: FontInstanceKey); } pub type BlobImageData = Vec; diff --git a/gfx/webrender_bindings/Cargo.toml b/gfx/webrender_bindings/Cargo.toml index 87a95ae2024d..73cf68977a9b 100644 --- a/gfx/webrender_bindings/Cargo.toml +++ b/gfx/webrender_bindings/Cargo.toml @@ -5,7 +5,7 @@ authors = ["The Mozilla Project Developers"] license = "MPL-2.0" [dependencies] -webrender_api = {path = "../webrender_api", version = "0.49.0"} +webrender_api = {path = "../webrender_api", version = "0.50.0"} rayon = "0.8" thread_profiler = "0.1.1" euclid = "0.15" @@ -14,5 +14,5 @@ gleam = "0.4" [dependencies.webrender] path = "../webrender" -version = "0.49.0" +version = "0.50.0" default-features = false diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index 578996111901..cc57d9b880f3 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -524,6 +524,22 @@ WebRenderAPI::DeleteFont(wr::FontKey aKey) wr_api_delete_font(mDocHandle, aKey); } +void +WebRenderAPI::AddFontInstance(wr::FontInstanceKey aKey, + wr::FontKey aFontKey, + float aGlyphSize, + const wr::FontInstanceOptions* aOptions, + const wr::FontInstancePlatformOptions* aPlatformOptions) +{ + wr_api_add_font_instance(mDocHandle, aKey, aFontKey, aGlyphSize, aOptions, aPlatformOptions); +} + +void +WebRenderAPI::DeleteFontInstance(wr::FontInstanceKey aKey) +{ + wr_api_delete_font_instance(mDocHandle, aKey); +} + class FrameStartTime : public RendererEvent { public: @@ -940,15 +956,15 @@ void DisplayListBuilder::PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, const gfx::Color& aColor, - wr::FontKey aFontKey, + wr::FontInstanceKey aFontKey, Range aGlyphBuffer, - float aGlyphSize) + const wr::GlyphOptions* aGlyphOptions) { wr_dp_push_text(mWrState, aBounds, aClip, ToColorF(aColor), aFontKey, &aGlyphBuffer[0], aGlyphBuffer.length(), - aGlyphSize); + aGlyphOptions); } void diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index b1ff4d063c81..3bc857f0a2f3 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -123,6 +123,14 @@ public: void DeleteFont(wr::FontKey aKey); + void AddFontInstance(wr::FontInstanceKey aKey, + wr::FontKey aFontKey, + float aGlyphSize, + const wr::FontInstanceOptions* aOptions, + const wr::FontInstancePlatformOptions* aPlatformOptions); + + void DeleteFontInstance(wr::FontInstanceKey aKey); + void SetFrameStartTime(const TimeStamp& aTime); void RunOnRenderThread(UniquePtr aEvent); @@ -302,9 +310,9 @@ public: void PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, const gfx::Color& aColor, - wr::FontKey aFontKey, + wr::FontInstanceKey aFontKey, Range aGlyphBuffer, - float aGlyphSize); + const wr::GlyphOptions* aGlyphOptions = nullptr); void PushLine(const wr::LayoutRect& aClip, const wr::Line& aLine); diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h index 7f146445c930..c3c83e3d0c31 100644 --- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -27,6 +27,7 @@ typedef wr::WrWindowId WindowId; typedef wr::WrPipelineId PipelineId; typedef wr::WrImageKey ImageKey; typedef wr::WrFontKey FontKey; +typedef wr::WrFontInstanceKey FontInstanceKey; typedef wr::WrEpoch Epoch; typedef wr::WrExternalImageId ExternalImageId; typedef wr::WrDebugFlags DebugFlags; @@ -34,6 +35,9 @@ typedef wr::WrDebugFlags DebugFlags; typedef mozilla::Maybe MaybeImageMask; typedef Maybe MaybeExternalImageId; +typedef Maybe MaybeFontInstanceOptions; +typedef Maybe MaybeFontInstancePlatformOptions; + inline WindowId NewWindowId(uint64_t aId) { WindowId id; id.mHandle = aId; @@ -141,6 +145,19 @@ inline FontKey AsFontKey(const uint64_t& aId) { return fontKey; } +// Whenever possible, use wr::FontInstanceKey instead of manipulating uint64_t. +inline uint64_t AsUint64(const FontInstanceKey& aId) { + return (static_cast(aId.mNamespace.mHandle) << 32) + + static_cast(aId.mHandle); +} + +inline FontInstanceKey AsFontInstanceKey(const uint64_t& aId) { + FontInstanceKey instanceKey; + instanceKey.mNamespace.mHandle = aId >> 32; + instanceKey.mHandle = aId; + return instanceKey; +} + // Whenever possible, use wr::PipelineId instead of manipulating uint64_t. inline uint64_t AsUint64(const PipelineId& aId) { return (static_cast(aId.mNamespace) << 32) diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index 6adff1246f05..e3bfbe146037 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -38,6 +38,8 @@ type WrImageKey = ImageKey; /// cbindgen:field-names=[mNamespace, mHandle] type WrFontKey = FontKey; /// cbindgen:field-names=[mNamespace, mHandle] +type WrFontInstanceKey = FontInstanceKey; +/// cbindgen:field-names=[mNamespace, mHandle] type WrYuvColorSpace = YuvColorSpace; fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] { @@ -906,6 +908,32 @@ pub extern "C" fn wr_api_delete_font(dh: &mut DocumentHandle, dh.api.update_resources(resources); } +#[no_mangle] +pub extern "C" fn wr_api_add_font_instance(dh: &mut DocumentHandle, + key: WrFontInstanceKey, + font_key: WrFontKey, + glyph_size: f32, + options: *const FontInstanceOptions, + platform_options: *const FontInstancePlatformOptions) { + assert!(unsafe { is_in_compositor_thread() }); + let mut resources = ResourceUpdates::new(); + resources.add_font_instance(key, + font_key, + Au::from_f32_px(glyph_size), + unsafe { options.as_ref().cloned() }, + unsafe { platform_options.as_ref().cloned() }); + dh.api.update_resources(resources); +} + +#[no_mangle] +pub extern "C" fn wr_api_delete_font_instance(dh: &mut DocumentHandle, + key: WrFontInstanceKey) { + assert!(unsafe { is_in_compositor_thread() }); + let mut resources = ResourceUpdates::new(); + resources.delete_font_instance(key); + dh.api.update_resources(resources); +} + #[no_mangle] pub unsafe extern "C" fn wr_api_get_namespace(dh: &mut DocumentHandle) -> WrIdNamespace { dh.api.get_namespace_id() @@ -1262,26 +1290,22 @@ pub extern "C" fn wr_dp_push_text(state: &mut WrState, bounds: LayoutRect, clip: LayoutRect, color: ColorF, - font_key: WrFontKey, + font_key: WrFontInstanceKey, glyphs: *const GlyphInstance, glyph_count: u32, - glyph_size: f32) { + glyph_options: *const GlyphOptions) { assert!(unsafe { is_in_main_thread() }); let glyph_slice = make_slice(glyphs, glyph_count as usize); - let colorf = ColorF::new(color.r, color.g, color.b, color.a); - - let glyph_options = None; // TODO state.frame_builder .dl_builder .push_text(bounds, Some(LocalClip::Rect(clip.into())), &glyph_slice, font_key, - colorf, - Au::from_f32_px(glyph_size), - glyph_options); + color, + unsafe { glyph_options.as_ref().cloned() }); } #[no_mangle] diff --git a/gfx/webrender_bindings/src/moz2d_renderer.rs b/gfx/webrender_bindings/src/moz2d_renderer.rs index 952b004ef01a..57005f4f635c 100644 --- a/gfx/webrender_bindings/src/moz2d_renderer.rs +++ b/gfx/webrender_bindings/src/moz2d_renderer.rs @@ -64,7 +64,7 @@ impl BlobImageRenderer for Moz2dImageRenderer { self.workers.spawn(move || { let buf_size = (descriptor.width * descriptor.height - * descriptor.format.bytes_per_pixel().unwrap()) as usize; + * descriptor.format.bytes_per_pixel()) as usize; let mut output = vec![255u8; buf_size]; let result = unsafe { @@ -120,8 +120,12 @@ impl BlobImageRenderer for Moz2dImageRenderer { // If we break out of the loop above it means the channel closed unexpectedly. Err(BlobImageError::Other("Channel closed".into())) } + fn delete_font(&mut self, _font: FontKey) { } + + fn delete_font_instance(&mut self, _key: FontInstanceKey) { + } } impl Moz2dImageRenderer { diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index a6c3d94ebb43..cd5932fbc39e 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -59,6 +59,14 @@ enum class ExternalImageType : uint32_t { Sentinel /* this must be last for serialization purposes. */ }; +enum class FontRenderMode : uint32_t { + Mono = 0, + Alpha = 1, + Subpixel = 2, + + Sentinel /* this must be last for serialization purposes. */ +}; + enum class ImageFormat : uint32_t { Invalid = 0, A8 = 1, @@ -244,6 +252,18 @@ struct WrExternalImageId { typedef ExternalImageType WrExternalImageBufferType; +struct FontInstanceKey { + IdNamespace mNamespace; + uint32_t mHandle; + + bool operator==(const FontInstanceKey& aOther) const { + return mNamespace == aOther.mNamespace && + mHandle == aOther.mHandle; + } +}; + +typedef FontInstanceKey WrFontInstanceKey; + struct FontKey { IdNamespace mNamespace; uint32_t mHandle; @@ -256,6 +276,24 @@ struct FontKey { typedef FontKey WrFontKey; +struct FontInstanceOptions { + FontRenderMode render_mode; + + bool operator==(const FontInstanceOptions& aOther) const { + return render_mode == aOther.render_mode; + } +}; + +struct FontInstancePlatformOptions { + bool use_embedded_bitmap; + bool force_gdi_rendering; + + bool operator==(const FontInstancePlatformOptions& aOther) const { + return use_embedded_bitmap == aOther.use_embedded_bitmap && + force_gdi_rendering == aOther.force_gdi_rendering; + } +}; + struct Epoch { uint32_t mHandle; @@ -592,6 +630,14 @@ struct GlyphInstance { } }; +struct GlyphOptions { + FontRenderMode render_mode; + + bool operator==(const GlyphOptions& aOther) const { + return render_mode == aOther.render_mode; + } +}; + struct TextShadow { LayoutVector2D offset; ColorF color; @@ -727,6 +773,15 @@ void wr_api_add_external_image(DocumentHandle *aDh, uint8_t aChannelIndex) WR_FUNC; +WR_INLINE +void wr_api_add_font_instance(DocumentHandle *aDh, + WrFontInstanceKey aKey, + WrFontKey aFontKey, + float aGlyphSize, + const FontInstanceOptions *aOptions, + const FontInstancePlatformOptions *aPlatformOptions) +WR_FUNC; + WR_INLINE void wr_api_add_image(DocumentHandle *aDh, WrImageKey aImageKey, @@ -762,6 +817,11 @@ void wr_api_delete_font(DocumentHandle *aDh, WrFontKey aKey) WR_FUNC; +WR_INLINE +void wr_api_delete_font_instance(DocumentHandle *aDh, + WrFontInstanceKey aKey) +WR_FUNC; + WR_INLINE void wr_api_delete_image(DocumentHandle *aDh, WrImageKey aKey) @@ -1055,10 +1115,10 @@ void wr_dp_push_text(WrState *aState, LayoutRect aBounds, LayoutRect aClip, ColorF aColor, - WrFontKey aFontKey, + WrFontInstanceKey aFontKey, const GlyphInstance *aGlyphs, uint32_t aGlyphCount, - float aGlyphSize) + const GlyphOptions *aGlyphOptions) WR_FUNC; WR_INLINE diff --git a/intl/lwbrk/nsILineBreaker.h b/intl/lwbrk/nsILineBreaker.h index e4ada94ad248..34cda30f4fed 100644 --- a/intl/lwbrk/nsILineBreaker.h +++ b/intl/lwbrk/nsILineBreaker.h @@ -55,12 +55,13 @@ NS_IsSpace(char16_t u) return u == 0x0020 || // SPACE u == 0x0009 || // CHARACTER TABULATION u == 0x000D || // CARRIAGE RETURN - u == 0x1680 || // OGHAM SPACE MARK (0x2000 <= u && u <= 0x2006) || // EN QUAD, EM QUAD, EN SPACE, // EM SPACE, THREE-PER-EM SPACE, // FOUR-PER-SPACE, SIX-PER-EM SPACE, (0x2008 <= u && u <= 0x200B) || // PUNCTUATION SPACE, THIN SPACE, // HAIR SPACE, ZERO WIDTH SPACE + u == 0x1361 || // ETHIOPIC WORDSPACE + u == 0x1680 || // OGHAM SPACE MARK u == 0x205F; // MEDIUM MATHEMATICAL SPACE } diff --git a/ipc/mscom/ProxyStream.cpp b/ipc/mscom/ProxyStream.cpp index 6bbf9b884161..a2b2336c82ed 100644 --- a/ipc/mscom/ProxyStream.cpp +++ b/ipc/mscom/ProxyStream.cpp @@ -127,6 +127,11 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf, CrashReporter::AnnotateCrashReport( NS_LITERAL_CSTRING("CoUnmarshalInterfaceResult"), hrAsStr); AnnotateInterfaceRegistration(aIID); + if (!mUnmarshaledProxy) { + CrashReporter::AnnotateCrashReport(kCrashReportKey, + NS_LITERAL_CSTRING("!mUnmarshaledProxy")); + } + #if defined(ACCESSIBILITY) AnnotateClassRegistration(CLSID_AccessibleHandler); CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("UnmarshalActCtx"), @@ -254,13 +259,6 @@ ProxyStream::GetInterface(void** aOutInterface) return false; } -#if defined(MOZ_CRASHREPORTER) - if (!mUnmarshaledProxy) { - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamUnmarshalStatus"), - NS_LITERAL_CSTRING("!mUnmarshaledProxy")); - } -#endif // defined(MOZ_CRASHREPORTER) - *aOutInterface = mUnmarshaledProxy.release(); return true; } diff --git a/js/moz.configure b/js/moz.configure index 4a8baf4d3342..5505fd1b7d42 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -22,12 +22,11 @@ option(env='JS_STANDALONE', default=building_js, def js_standalone(value): if value: return True - set_config('JS_STANDALONE', js_standalone) +set_define('JS_STANDALONE', js_standalone) add_old_configure_assignment('JS_STANDALONE', js_standalone) - js_option('--disable-js-shell', default=building_js, - help='Do not build the JS shell') + help='Do not build the JS shell') @depends('--disable-js-shell') def js_disable_shell(value): diff --git a/js/src/build.rs b/js/src/build.rs index bf23fc0461a7..97b57588265f 100644 --- a/js/src/build.rs +++ b/js/src/build.rs @@ -25,7 +25,12 @@ fn main() { let python = env::var("PYTHON").unwrap_or("python2.7".into()); let mut cmd = Command::new(&python); cmd.args(&["./devtools/automation/autospider.py", + // Only build SpiderMonkey, don't run all the tests. "--build-only", + // Disable Mozilla's jemalloc; Rust has its own jemalloc that we + // can swap in instead and everything using a single malloc is + // good. + "--no-jemalloc", "--objdir", &out_dir, variant]) .env("SOURCE", &js_src) diff --git a/js/src/devtools/automation/autospider.py b/js/src/devtools/automation/autospider.py index b5c3695d8ca3..f417f55f037c 100755 --- a/js/src/devtools/automation/autospider.py +++ b/js/src/devtools/automation/autospider.py @@ -61,6 +61,14 @@ group.add_argument('--no-debug', action='store_false', dest='debug', help='generate a non-debug build. Overrides variant setting.') group.set_defaults(debug=None) +group = parser.add_mutually_exclusive_group() +group.add_argument('--jemalloc', action='store_true', + dest='jemalloc', + help='use mozilla\'s jemalloc instead of the default allocator') +group.add_argument('--no-jemalloc', action='store_false', + dest='jemalloc', + help='use the default allocator instead of mozilla\'s jemalloc') +group.set_defaults(jemalloc=None) parser.add_argument('--run-tests', '--tests', type=str, metavar='TESTSUITE', default='', help="comma-separated set of test suites to add to the variant's default set") @@ -176,6 +184,10 @@ if opt is None: if opt is not None: CONFIGURE_ARGS += (" --enable-debug" if opt else " --disable-debug") +opt = args.jemalloc +if opt is not None: + CONFIGURE_ARGS += (" --enable-jemalloc" if opt else " --disable-jemalloc") + # Any jobs that wish to produce additional output can save them into the upload # directory if there is such a thing, falling back to OBJDIR. env.setdefault('MOZ_UPLOAD_DIR', OBJDIR) diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 2aef42ccfa27..b270a8c2409d 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -1495,16 +1495,20 @@ dnl ======================================================== dnl = Enable jemalloc dnl ======================================================== -case "${OS_TARGET}" in -Android|WINNT|Darwin) +if test "$JS_STANDALONE" -a -z "$MOZ_MEMORY"; then MOZ_GLUE_IN_PROGRAM= - ;; -*) - dnl On !Android !Windows !OSX, we only want to link executables against mozglue - MOZ_GLUE_IN_PROGRAM=1 - AC_DEFINE(MOZ_GLUE_IN_PROGRAM) - ;; -esac +else + case "${OS_TARGET}" in + Android|WINNT|Darwin) + MOZ_GLUE_IN_PROGRAM= + ;; + *) + dnl On !Android !Windows !OSX, we only want to link executables against mozglue + MOZ_GLUE_IN_PROGRAM=1 + AC_DEFINE(MOZ_GLUE_IN_PROGRAM) + ;; + esac +fi if test "$MOZ_MEMORY"; then dnl The generic feature tests that determine how to compute ncpus are long and diff --git a/js/xpconnect/loader/AutoMemMap.cpp b/js/xpconnect/loader/AutoMemMap.cpp index 92bc728c243c..466e2db64e29 100644 --- a/js/xpconnect/loader/AutoMemMap.cpp +++ b/js/xpconnect/loader/AutoMemMap.cpp @@ -45,7 +45,7 @@ AutoMemMap::init(nsIFile* file, int flags, int mode, PRFileMapProtect prot) { MOZ_ASSERT(!fd); - NS_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget())); + MOZ_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget())); return initInternal(prot); } @@ -76,7 +76,7 @@ AutoMemMap::initInternal(PRFileMapProtect prot) MOZ_ASSERT(!addr); PRFileInfo64 fileInfo; - NS_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo)); + MOZ_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo)); if (fileInfo.size > UINT32_MAX) return Err(NS_ERROR_INVALID_ARG); diff --git a/js/xpconnect/loader/ScriptPreloader-inl.h b/js/xpconnect/loader/ScriptPreloader-inl.h index 6b0c1deec4cf..193e5e8813f6 100644 --- a/js/xpconnect/loader/ScriptPreloader-inl.h +++ b/js/xpconnect/loader/ScriptPreloader-inl.h @@ -11,7 +11,7 @@ #include "mozilla/CheckedInt.h" #include "mozilla/EnumSet.h" #include "mozilla/Range.h" -#include "mozilla/Result.h" +#include "mozilla/ResultExtensions.h" #include "mozilla/Unused.h" #include "mozilla/dom/ScriptSettings.h" #include "nsString.h" @@ -21,21 +21,6 @@ namespace mozilla { -// A specialization of GenericErrorResult which auto-converts to a nsresult. -// This should be removed when bug 1366511 is fixed. -template <> -class MOZ_MUST_USE_TYPE GenericErrorResult -{ - nsresult mErrorValue; - - template friend class Result; - -public: - explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {} - - operator nsresult() { return mErrorValue; } -}; - namespace loader { using mozilla::dom::AutoJSAPI; @@ -45,26 +30,6 @@ struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI AutoSafeJSAPI() { Init(); } }; -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - class OutputBuffer { diff --git a/js/xpconnect/loader/ScriptPreloader.cpp b/js/xpconnect/loader/ScriptPreloader.cpp index 437c11bc48b8..f1c537950ba8 100644 --- a/js/xpconnect/loader/ScriptPreloader.cpp +++ b/js/xpconnect/loader/ScriptPreloader.cpp @@ -4,8 +4,8 @@ * 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 "mozilla/ScriptPreloader.h" #include "ScriptPreloader-inl.h" +#include "mozilla/ScriptPreloader.h" #include "mozilla/loader/ScriptCacheActors.h" #include "mozilla/ArrayUtils.h" @@ -362,12 +362,12 @@ Result, nsresult> ScriptPreloader::GetCacheFile(const nsAString& suffix) { nsCOMPtr cacheFile; - NS_TRY(mProfD->Clone(getter_AddRefs(cacheFile))); + MOZ_TRY(mProfD->Clone(getter_AddRefs(cacheFile))); - NS_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache"))); + MOZ_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache"))); Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777); - NS_TRY(cacheFile->Append(mBaseName + suffix)); + MOZ_TRY(cacheFile->Append(mBaseName + suffix)); return Move(cacheFile); } @@ -377,18 +377,18 @@ static const uint8_t MAGIC[] = "mozXDRcachev001"; Result ScriptPreloader::OpenCache() { - NS_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD))); + MOZ_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD))); nsCOMPtr cacheFile; MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING(".bin"))); bool exists; - NS_TRY(cacheFile->Exists(&exists)); + MOZ_TRY(cacheFile->Exists(&exists)); if (exists) { - NS_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING("-current.bin"))); + MOZ_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING("-current.bin"))); } else { - NS_TRY(cacheFile->SetLeafName(mBaseName + NS_LITERAL_STRING("-current.bin"))); - NS_TRY(cacheFile->Exists(&exists)); + MOZ_TRY(cacheFile->SetLeafName(mBaseName + NS_LITERAL_STRING("-current.bin"))); + MOZ_TRY(cacheFile->Exists(&exists)); if (!exists) { return Err(NS_ERROR_FILE_NOT_FOUND); } @@ -627,14 +627,14 @@ ScriptPreloader::WriteCache() MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING("-new.bin"))); bool exists; - NS_TRY(cacheFile->Exists(&exists)); + MOZ_TRY(cacheFile->Exists(&exists)); if (exists) { - NS_TRY(cacheFile->Remove(false)); + MOZ_TRY(cacheFile->Remove(false)); } { AutoFDClose fd; - NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget())); + MOZ_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget())); // We also need to hold mMonitor while we're touching scripts in // mScripts, or they may be freed before we're done with them. @@ -675,7 +675,7 @@ ScriptPreloader::WriteCache() } } - NS_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING(".bin"))); + MOZ_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING(".bin"))); return Ok(); } diff --git a/layout/base/GeckoRestyleManager.cpp b/layout/base/GeckoRestyleManager.cpp index 610de53f5b44..45f9cb03b2b1 100644 --- a/layout/base/GeckoRestyleManager.cpp +++ b/layout/base/GeckoRestyleManager.cpp @@ -1024,16 +1024,13 @@ GeckoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) #endif if (!newParentContext && !oldContext->GetParent()) { - // No need to do anything here. -#ifdef DEBUG - // Make sure we have no children, so we really know there is nothing to do. - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - MOZ_ASSERT(lists.CurrentList().IsEmpty(), - "Failing to reparent style context for child of " - "non-inheriting anon box"); - } -#endif // DEBUG + // No need to do anything here for this frame, but we should still reparent + // its descendants, because those may have styles that inherit from the + // parent of this frame (e.g. non-anonymous columns in an anonymous + // colgroup). + MOZ_ASSERT(aFrame->StyleContext()->IsNonInheritingAnonBox(), + "Why did this frame not end up with a parent context?"); + ReparentFrameDescendants(aFrame, providerChild); return NS_OK; } @@ -1091,26 +1088,7 @@ GeckoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) aFrame->SetStyleContext(newContext); - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - for (nsIFrame* child : lists.CurrentList()) { - // only do frames that are in flow - if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && - child != providerChild) { -#ifdef DEBUG - if (child->IsPlaceholderFrame()) { - nsIFrame* outOfFlowFrame = - nsPlaceholderFrame::GetRealFrameForPlaceholder(child); - NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame"); - - NS_ASSERTION(outOfFlowFrame != providerChild, - "Out of flow provider?"); - } -#endif - ReparentStyleContext(child); - } - } - } + ReparentFrameDescendants(aFrame, providerChild); // If this frame is part of an IB split, then the style context of // the next part of the split might be a child of our style context. @@ -1154,6 +1132,32 @@ GeckoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) return NS_OK; } +void +GeckoRestyleManager::ReparentFrameDescendants(nsIFrame* aFrame, + nsIFrame* aProviderChild) +{ + nsIFrame::ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + for (nsIFrame* child : lists.CurrentList()) { + // only do frames that are in flow + if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && + child != aProviderChild) { +#ifdef DEBUG + if (child->IsPlaceholderFrame()) { + nsIFrame* outOfFlowFrame = + nsPlaceholderFrame::GetRealFrameForPlaceholder(child); + NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame"); + + NS_ASSERTION(outOfFlowFrame != aProviderChild, + "Out of flow provider?"); + } +#endif + ReparentStyleContext(child); + } + } + } +} + ElementRestyler::ElementRestyler(nsPresContext* aPresContext, nsIFrame* aFrame, nsStyleChangeList* aChangeList, diff --git a/layout/base/GeckoRestyleManager.h b/layout/base/GeckoRestyleManager.h index d8d7e9268f2f..086ef04822e1 100644 --- a/layout/base/GeckoRestyleManager.h +++ b/layout/base/GeckoRestyleManager.h @@ -88,6 +88,16 @@ public: */ nsresult ReparentStyleContext(nsIFrame* aFrame); +private: + /** + * Reparent the descendants of aFrame. This is used by ReparentStyleContext + * and shouldn't be called by anyone else. aProviderChild, if non-null, is a + * child that was the style parent for aFrame and hence shouldn't be + * reparented. + */ + void ReparentFrameDescendants(nsIFrame* aFrame, nsIFrame* aProviderChild); + +public: void ClearSelectors() { mPendingRestyles.ClearSelectors(); } diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index e5ccaf9ecdb6..076df1e8f698 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -1488,20 +1488,6 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, nsIFrame* providerFrame; nsStyleContext* newParentContext = aFrame->GetParentStyleContext(&providerFrame); - if (!newParentContext) { - // No need to do anything here. -#ifdef DEBUG - // Make sure we have no children, so we really know there is nothing to do. - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - MOZ_ASSERT(lists.CurrentList().IsEmpty(), - "Failing to reparent style context for child of " - "non-inheriting anon box"); - } -#endif // DEBUG - return; - } - // If our provider is our child, we want to reparent it first, because we // inherit style from it. bool isChild = providerFrame && providerFrame->GetParent() == aFrame; @@ -1516,6 +1502,17 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, "Out of flow provider?"); } + if (!newParentContext) { + // No need to do anything here for this frame, but we should still reparent + // its descendants, because those may have styles that inherit from the + // parent of this frame (e.g. non-anonymous columns in an anonymous + // colgroup). + MOZ_ASSERT(aFrame->StyleContext()->IsNonInheritingAnonBox(), + "Why did this frame not end up with a parent context?"); + ReparentFrameDescendants(aFrame, providerChild, aStyleSet); + return; + } + bool isElement = aFrame->GetContent()->IsElement(); // We probably don't want to initiate transitions from @@ -1554,9 +1551,15 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, if (!providerFrame) { // No providerFrame means we inherited from a display:contents thing. Our - // layout parent style is the style of our nearest ancestor frame. - providerFrame = nsFrame::CorrectStyleParentFrame(aFrame->GetParent(), - oldContext->GetPseudo()); + // layout parent style is the style of our nearest ancestor frame. But we have + // to be careful to do that with our placeholder, not with us, if we're out of + // flow. + if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { + aFrame->GetPlaceholderFrame()->GetLayoutParentStyleForOutOfFlow(&providerFrame); + } else { + providerFrame = nsFrame::CorrectStyleParentFrame(aFrame->GetParent(), + oldContext->GetPseudo()); + } } ServoStyleContext* layoutParent = providerFrame->StyleContext()->AsServo(); @@ -1599,19 +1602,27 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, // reparenting the table wrapper frame. So no need to // UpdateStyleOfOwnedAnonBoxes() here. - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - for (nsIFrame* child : lists.CurrentList()) { - // only do frames that are in flow - if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && - child != providerChild) { - DoReparentStyleContext(child, aStyleSet); - } - } - } + ReparentFrameDescendants(aFrame, providerChild, aStyleSet); // We do not need to do the equivalent of UpdateFramePseudoElementStyles, // because those are hadled by our descendant walk. } +void +ServoRestyleManager::ReparentFrameDescendants(nsIFrame* aFrame, + nsIFrame* aProviderChild, + ServoStyleSet& aStyleSet) +{ + nsIFrame::ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + for (nsIFrame* child : lists.CurrentList()) { + // only do frames that are in flow + if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && + child != aProviderChild) { + DoReparentStyleContext(child, aStyleSet); + } + } + } +} + } // namespace mozilla diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h index 51be8dabae55..f9ac36fadb3b 100644 --- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.h @@ -221,6 +221,17 @@ public: // this method accordingly (e.g. to ReparentStyleContextForFirstLine). nsresult ReparentStyleContext(nsIFrame* aFrame); +private: + /** + * Reparent the descendants of aFrame. This is used by ReparentStyleContext + * and shouldn't be called by anyone else. aProviderChild, if non-null, is a + * child that was the style parent for aFrame and hence shouldn't be + * reparented. + */ + void ReparentFrameDescendants(nsIFrame* aFrame, nsIFrame* aProviderChild, + ServoStyleSet& aStyleSet); + +public: /** * Clears the ServoElementData and HasDirtyDescendants from all elements * in the subtree rooted at aElement. diff --git a/layout/base/crashtests/1395591-1.html b/layout/base/crashtests/1395591-1.html new file mode 100644 index 000000000000..2fca2ab4d098 --- /dev/null +++ b/layout/base/crashtests/1395591-1.html @@ -0,0 +1,11 @@ + + +
diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index bd0d762a39b2..2e9f80cc84c8 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -495,3 +495,4 @@ load 1362423-1.html load 1381323.html asserts-if(!stylo,1) load 1388625-1.html # bug 1389286 load 1390389.html +load 1395591-1.html diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index e957f0aac5b2..54a809f76254 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -10247,7 +10247,8 @@ nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = { FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS | FCDATA_SKIP_ABSPOS_PUSH | - FCDATA_IS_WRAPPER_ANON_BOX | + // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to + // restyle these: they have non-inheriting style contexts. FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), NS_NewTableColGroupFrame), &nsCSSAnonBoxes::tableColGroup @@ -10886,9 +10887,16 @@ nsCSSFrameConstructor::WrapItemsInPseudoParent(nsIContent* aParentContent, pseudoType = nsCSSAnonBoxes::inlineTable; } - already_AddRefed wrapperStyle = - mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType, - aParentStyle); + already_AddRefed wrapperStyle; + if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) { + wrapperStyle = + mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType, + aParentStyle); + } else { + wrapperStyle = + mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(pseudoType); + } + FrameConstructionItem* newItem = new FrameConstructionItem(&pseudoData.mFCData, // Use the content of our parent frame diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index 576b67e62507..487af4eda75c 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -203,6 +203,12 @@ nsPlaceholderFrame::GetParentStyleContextForOutOfFlow(nsIFrame** aProviderFrame) } } + return GetLayoutParentStyleForOutOfFlow(aProviderFrame); +} + +nsStyleContext* +nsPlaceholderFrame::GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const +{ nsIFrame* parentFrame = GetParent(); // Placeholder of backdrop frame is a child of the corresponding top // layer frame, and its style context inherits from that frame. In diff --git a/layout/generic/nsPlaceholderFrame.h b/layout/generic/nsPlaceholderFrame.h index 737522c2febe..e270c76cdd31 100644 --- a/layout/generic/nsPlaceholderFrame.h +++ b/layout/generic/nsPlaceholderFrame.h @@ -146,6 +146,9 @@ public: nsStyleContext* GetParentStyleContextForOutOfFlow(nsIFrame** aProviderFrame) const; + // Like GetParentStyleContextForOutOfFlow, but ignores display:contents bits. + nsStyleContext* GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const; + bool RenumberFrameAndDescendants(int32_t* aOrdinal, int32_t aDepth, int32_t aIncrement, diff --git a/layout/reftests/bugs/1395650-1-ref.html b/layout/reftests/bugs/1395650-1-ref.html new file mode 100644 index 000000000000..a3c22b280e90 --- /dev/null +++ b/layout/reftests/bugs/1395650-1-ref.html @@ -0,0 +1,4 @@ + + + +
This text should be visible
diff --git a/layout/reftests/bugs/1395650-1.html b/layout/reftests/bugs/1395650-1.html new file mode 100644 index 000000000000..1ad68348b36f --- /dev/null +++ b/layout/reftests/bugs/1395650-1.html @@ -0,0 +1,6 @@ + + + + + +
This text should be visible
diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index daedfce6217a..db9de2f259bb 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -2038,3 +2038,4 @@ needs-focus != 1377447-1.html 1377447-2.html == 1384065.html 1384065-ref.html == 1384275-1.html 1384275-1-ref.html == 1381821.html 1381821-ref.html +== 1395650-1.html 1395650-1-ref.html diff --git a/layout/reftests/css-break/reftest.list b/layout/reftests/css-break/reftest.list index e86f5bd4f07e..cfb03d2062fb 100644 --- a/layout/reftests/css-break/reftest.list +++ b/layout/reftests/css-break/reftest.list @@ -1,7 +1,7 @@ default-preferences pref(layout.css.box-decoration-break.enabled,true) == box-decoration-break-1.html box-decoration-break-1-ref.html -fuzzy(1,20) fuzzy-if(skiaContent,1,700) fuzzy-if(webrender,4-4,3320-3320) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html +fuzzy(1,20) fuzzy-if(skiaContent,1,700) fuzzy-if(webrender,4-4,83-83) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html fuzzy(16,460) fuzzy-if(skiaContent,57,374) fuzzy-if(Android,57,1330) fuzzy-if(styloVsGecko,2,1410) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543 random-if(!gtkWidget) HTTP(..) == box-decoration-break-border-image.html box-decoration-break-border-image-ref.html == box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding-ref.html diff --git a/layout/reftests/text/ethiopic-wordspace-ref.html b/layout/reftests/text/ethiopic-wordspace-ref.html new file mode 100644 index 000000000000..39c289b5e6cc --- /dev/null +++ b/layout/reftests/text/ethiopic-wordspace-ref.html @@ -0,0 +1,17 @@ + + + + + Reference: Testcase for bug 815077 + + + +

+የሰው፡
ልጅ፡
ሁሉ፡
ሲወለድ፡
ነጻና፡
በክብርና፡
በመብትም፡
እኩልነት፡
ያለው፡
ነው።፡
የተፈጥሮ፡
ማስተዋልና፡
ሕሊና፡
ስላለው፡
አንዱ፡
ሌላውን፡
በወንድማማችነት፡
መንፈስ፡
መመልከት፡
ይገባዋል። +

+ + + diff --git a/layout/reftests/text/ethiopic-wordspace.html b/layout/reftests/text/ethiopic-wordspace.html new file mode 100644 index 000000000000..912374e34e7e --- /dev/null +++ b/layout/reftests/text/ethiopic-wordspace.html @@ -0,0 +1,17 @@ + + + + + Testcase for bug 815077 + + + +

+የሰው፡ልጅ፡ሁሉ፡ሲወለድ፡ነጻና፡በክብርና፡በመብትም፡እኩልነት፡ያለው፡ነው።፡የተፈጥሮ፡ማስተዋልና፡ሕሊና፡ስላለው፡አንዱ፡ሌላውን፡በወንድማማችነት፡መንፈስ፡መመልከት፡ይገባዋል። +

+ + + diff --git a/layout/reftests/text/reftest.list b/layout/reftests/text/reftest.list index 198cb900856b..0d721fa74fa7 100644 --- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -354,3 +354,5 @@ HTTP(..) == space-font-1.html space-font-1-ref.html == letter-spacing-nolig-1.html letter-spacing-nolig-1-ref.html HTTP(..) == letter-spacing-nolig-2.html letter-spacing-nolig-2.html + +== ethiopic-wordspace.html ethiopic-wordspace-ref.html diff --git a/layout/style/nsCSSAnonBoxList.h b/layout/style/nsCSSAnonBoxList.h index f79aa51beadb..e86726a451a2 100644 --- a/layout/style/nsCSSAnonBoxList.h +++ b/layout/style/nsCSSAnonBoxList.h @@ -81,7 +81,7 @@ CSS_ANON_BOX(htmlCanvasContent, ":-moz-html-canvas-content") CSS_WRAPPER_ANON_BOX(inlineTable, ":-moz-inline-table") CSS_WRAPPER_ANON_BOX(table, ":-moz-table") CSS_WRAPPER_ANON_BOX(tableCell, ":-moz-table-cell") -CSS_WRAPPER_ANON_BOX(tableColGroup, ":-moz-table-column-group") +CSS_NON_INHERITING_ANON_BOX(tableColGroup, ":-moz-table-column-group") CSS_NON_INHERITING_ANON_BOX(tableCol, ":-moz-table-column") CSS_ANON_BOX(tableWrapper, ":-moz-table-wrapper") CSS_WRAPPER_ANON_BOX(tableRowGroup, ":-moz-table-row-group") diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 961669771104..64487781027b 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -688,8 +688,7 @@ nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType) RefPtr colGroupStyle; colGroupStyle = shell->StyleSet()-> - ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, - mStyleContext); + ResolveNonInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup); // Create a col group frame nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle); ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType); @@ -8051,27 +8050,6 @@ nsTableFrame::AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) "What happened to our parent?"); aResult.AppendElement( OwnedAnonBox(wrapper, &UpdateStyleOfOwnedAnonBoxesForTableWrapper)); - - // We may also have an anonymous colgroup that we're responsible for. - // Specifically, we can have three types of colgroup frames: (A) corresponding - // to actual elements with "display: table-column-group", (B) wrapping runs of - // "display: table-column" kids, and (C) one colgroup frame added at the end - // to hold the anonymous colframes we need so each cell has an associated - // colframe. - // - // These types of colgroups are supposed to correspond to the values of the - // nsTableColGroupType enum: type (A) to eColGroupContent, type (B) to - // eColGroupAnonymousCol, and type (C) to eColGroupAnonymousCell. But we - // never actually set eColGroupAnonymousCol on any colgroups right now; see - // bug 1387568. In any case, eColGroupAnonymousCell works correctly to detect - // colgroups of type (C), which are the ones we want to restyle here. Type - // (A) will be restyled via their element, and type (B) via the machinery for - // restyling wrapper anonymous frames. - auto colGroupFrame = - static_cast(mColGroups.LastChild()); - if (colGroupFrame && colGroupFrame->GetColType() == eColGroupAnonymousCell) { - aResult.AppendElement(colGroupFrame); - } } /* static */ void diff --git a/memory/build/malloc_decls.h b/memory/build/malloc_decls.h index 2d71badd3c68..a0dd9b9ca219 100644 --- a/memory/build/malloc_decls.h +++ b/memory/build/malloc_decls.h @@ -62,6 +62,7 @@ MALLOC_DECL_VOID(jemalloc_stats, jemalloc_stats_t *) MALLOC_DECL_VOID(jemalloc_purge_freed_pages) MALLOC_DECL_VOID(jemalloc_free_dirty_pages) MALLOC_DECL_VOID(jemalloc_thread_local_arena, bool) +MALLOC_DECL_VOID(jemalloc_ptr_info, const void*, jemalloc_ptr_info_t*) # endif # undef MALLOC_DECL_VOID diff --git a/memory/build/mozmemory.h b/memory/build/mozmemory.h index 5c7565f8d4d4..4310f8c2fbb2 100644 --- a/memory/build/mozmemory.h +++ b/memory/build/mozmemory.h @@ -13,6 +13,7 @@ * - jemalloc_purge_freed_pages * - jemalloc_free_dirty_pages * - jemalloc_thread_local_arena + * - jemalloc_ptr_info */ #ifndef MOZ_MEMORY @@ -87,4 +88,10 @@ MOZ_JEMALLOC_API void jemalloc_free_dirty_pages(); MOZ_JEMALLOC_API void jemalloc_thread_local_arena(bool enabled); +/* + * Provide information about any allocation enclosing the given address. + */ +MOZ_JEMALLOC_API void jemalloc_ptr_info(const void* ptr, + jemalloc_ptr_info_t* info); + #endif /* mozmemory_h */ diff --git a/memory/build/mozmemory_wrap.c b/memory/build/mozmemory_wrap.c index 3d813d5a7372..81b41ccb9a56 100644 --- a/memory/build/mozmemory_wrap.c +++ b/memory/build/mozmemory_wrap.c @@ -10,6 +10,7 @@ * argument types. */ #define MALLOC_DECL(name, return_type, ...) \ MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__); +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC #include "malloc_decls.h" #ifdef MOZ_WRAP_NEW_DELETE diff --git a/memory/build/mozmemory_wrap.h b/memory/build/mozmemory_wrap.h index c537b99495f2..40b7c44e9fae 100644 --- a/memory/build/mozmemory_wrap.h +++ b/memory/build/mozmemory_wrap.h @@ -37,6 +37,7 @@ * - jemalloc_purge_freed_pages * - jemalloc_free_dirty_pages * - jemalloc_thread_local_arena + * - jemalloc_ptr_info * (these functions are native to mozjemalloc) * * These functions are all exported as part of libmozglue (see @@ -207,5 +208,7 @@ #define jemalloc_free_dirty_pages_impl mozmem_jemalloc_impl(jemalloc_free_dirty_pages) #define jemalloc_thread_local_arena_impl \ mozmem_jemalloc_impl(jemalloc_thread_local_arena) +#define jemalloc_ptr_info_impl \ + mozmem_jemalloc_impl(jemalloc_ptr_info) #endif /* mozmemory_wrap_h */ diff --git a/memory/gtest/TestJemalloc.cpp b/memory/gtest/TestJemalloc.cpp index f37c57376a79..ce8da8e7e203 100644 --- a/memory/gtest/TestJemalloc.cpp +++ b/memory/gtest/TestJemalloc.cpp @@ -1,40 +1,45 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/mozalloc.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" #include "mozmemory.h" #include "gtest/gtest.h" +using namespace mozilla; + static inline void TestOne(size_t size) { - size_t req = size; - size_t adv = malloc_good_size(req); - char* p = (char*)malloc(req); - size_t usable = moz_malloc_usable_size(p); - // NB: Using EXPECT here so that we still free the memory on failure. - EXPECT_EQ(adv, usable) << - "malloc_good_size(" << req << ") --> " << adv << "; " - "malloc_usable_size(" << req << ") --> " << usable; - free(p); + size_t req = size; + size_t adv = malloc_good_size(req); + char* p = (char*)malloc(req); + size_t usable = moz_malloc_usable_size(p); + // NB: Using EXPECT here so that we still free the memory on failure. + EXPECT_EQ(adv, usable) << + "malloc_good_size(" << req << ") --> " << adv << "; " + "malloc_usable_size(" << req << ") --> " << usable; + free(p); } static inline void TestThree(size_t size) { - ASSERT_NO_FATAL_FAILURE(TestOne(size - 1)); - ASSERT_NO_FATAL_FAILURE(TestOne(size)); - ASSERT_NO_FATAL_FAILURE(TestOne(size + 1)); + ASSERT_NO_FATAL_FAILURE(TestOne(size - 1)); + ASSERT_NO_FATAL_FAILURE(TestOne(size)); + ASSERT_NO_FATAL_FAILURE(TestOne(size + 1)); } +#define K * 1024 +#define M * 1024 * 1024 + TEST(Jemalloc, UsableSizeInAdvance) { - #define K * 1024 - #define M * 1024 * 1024 - /* * Test every size up to a certain point, then (N-1, N, N+1) triplets for a * various sizes beyond that. @@ -49,3 +54,177 @@ TEST(Jemalloc, UsableSizeInAdvance) for (size_t n = 1 M; n < 8 M; n += 128 K) ASSERT_NO_FATAL_FAILURE(TestThree(n)); } + +static int gStaticVar; + +bool InfoEq(jemalloc_ptr_info_t& aInfo, PtrInfoTag aTag, void* aAddr, + size_t aSize) +{ + return aInfo.tag == aTag && aInfo.addr == aAddr && aInfo.size == aSize; +} + +bool InfoEqFreedPage(jemalloc_ptr_info_t& aInfo, void* aAddr, size_t aPageSize) +{ + size_t pageSizeMask = aPageSize - 1; + + return jemalloc_ptr_is_freed_page(&aInfo) && + aInfo.addr == (void*)(uintptr_t(aAddr) & ~pageSizeMask) && + aInfo.size == aPageSize; +} + +TEST(Jemalloc, PtrInfo) +{ + // Some things might be running in other threads, so ensure our assumptions + // (e.g. about isFreedSmall and isFreedPage ratios below) are not altered by + // other threads. + jemalloc_thread_local_arena(true); + + jemalloc_stats_t stats; + jemalloc_stats(&stats); + + jemalloc_ptr_info_t info; + Vector small, large, huge; + + // For small (<= 2KiB) allocations, test every position within many possible + // sizes. + size_t small_max = stats.page_size / 2; + for (size_t n = 0; n <= small_max; n += 8) { + auto p = (char*)malloc(n); + size_t usable = moz_malloc_size_of(p); + ASSERT_TRUE(small.append(p)); + for (size_t j = 0; j < usable; j++) { + jemalloc_ptr_info(&p[j], &info); + ASSERT_TRUE(InfoEq(info, TagLiveSmall, p, usable)); + } + } + + // Similar for large (2KiB + 1 KiB .. 1MiB - 8KiB) allocations. + for (size_t n = small_max + 1 K; n <= stats.large_max; n += 1 K) { + auto p = (char*)malloc(n); + size_t usable = moz_malloc_size_of(p); + ASSERT_TRUE(large.append(p)); + for (size_t j = 0; j < usable; j += 347) { + jemalloc_ptr_info(&p[j], &info); + ASSERT_TRUE(InfoEq(info, TagLiveLarge, p, usable)); + } + } + + // Similar for huge (> 1MiB - 8KiB) allocations. + for (size_t n = stats.chunksize; n <= 10 M; n += 512 K) { + auto p = (char*)malloc(n); + size_t usable = moz_malloc_size_of(p); + ASSERT_TRUE(huge.append(p)); + for (size_t j = 0; j < usable; j += 567) { + jemalloc_ptr_info(&p[j], &info); + ASSERT_TRUE(InfoEq(info, TagLiveHuge, p, usable)); + } + } + + // The following loops check freed allocations. We step through the vectors + // using prime-sized steps, which gives full coverage of the arrays while + // avoiding deallocating in the same order we allocated. + size_t len; + + // Free the small allocations and recheck them. + int isFreedSmall = 0, isFreedPage = 0; + len = small.length(); + for (size_t i = 0, j = 0; i < len; i++, j = (j + 19) % len) { + char* p = small[j]; + size_t usable = moz_malloc_size_of(p); + free(p); + for (size_t k = 0; k < usable; k++) { + jemalloc_ptr_info(&p[k], &info); + // There are two valid outcomes here. + if (InfoEq(info, TagFreedSmall, p, usable)) { + isFreedSmall++; + } else if (InfoEqFreedPage(info, &p[k], stats.page_size)) { + isFreedPage++; + } else { + ASSERT_TRUE(false); + } + } + } + // There should be both FreedSmall and FreedPage results, but a lot more of + // the former. + ASSERT_TRUE(isFreedSmall != 0); + ASSERT_TRUE(isFreedPage != 0); + ASSERT_TRUE(isFreedSmall / isFreedPage > 10); + + // Free the large allocations and recheck them. + len = large.length(); + for (size_t i = 0, j = 0; i < len; i++, j = (j + 31) % len) { + char* p = large[j]; + size_t usable = moz_malloc_size_of(p); + free(p); + for (size_t k = 0; k < usable; k += 357) { + jemalloc_ptr_info(&p[k], &info); + ASSERT_TRUE(InfoEqFreedPage(info, &p[k], stats.page_size)); + } + } + + // Free the huge allocations and recheck them. + len = huge.length(); + for (size_t i = 0, j = 0; i < len; i++, j = (j + 7) % len) { + char* p = huge[j]; + size_t usable = moz_malloc_size_of(p); + free(p); + for (size_t k = 0; k < usable; k += 587) { + jemalloc_ptr_info(&p[k], &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + } + } + + // Null ptr. + jemalloc_ptr_info(nullptr, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Near-null ptr. + jemalloc_ptr_info((void*)0x123, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Maximum address. + jemalloc_ptr_info((void*)uintptr_t(-1), &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Stack memory. + int stackVar; + jemalloc_ptr_info(&stackVar, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Code memory. + jemalloc_ptr_info((const void*)&jemalloc_ptr_info, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Static memory. + jemalloc_ptr_info(&gStaticVar, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Chunk header. + UniquePtr p = MakeUnique(); + size_t chunksizeMask = stats.chunksize - 1; + char* chunk = (char*)(uintptr_t(p.get()) & ~chunksizeMask); + size_t chunkHeaderSize = stats.chunksize - stats.large_max; + for (size_t i = 0; i < chunkHeaderSize; i += 64) { + jemalloc_ptr_info(&chunk[i], &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + } + + // Run header. + size_t page_sizeMask = stats.page_size - 1; + char* run = (char*)(uintptr_t(p.get()) & ~page_sizeMask); + for (size_t i = 0; i < 4 * sizeof(void*); i++) { + jemalloc_ptr_info(&run[i], &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + } + + // Entire chunk. It's impossible to check what is put into |info| for all of + // these addresses; this is more about checking that we don't crash. + for (size_t i = 0; i < stats.chunksize; i += 256) { + jemalloc_ptr_info(&chunk[i], &info); + } + + jemalloc_thread_local_arena(false); +} + +#undef K +#undef M diff --git a/memory/mozalloc/mozalloc.cpp b/memory/mozalloc/mozalloc.cpp index 1124e37a545a..764113aa6aa8 100644 --- a/memory/mozalloc/mozalloc.cpp +++ b/memory/mozalloc/mozalloc.cpp @@ -66,6 +66,7 @@ MOZ_MEMORY_API char *strndup_impl(const char *, size_t); #include +#include "mozilla/Assertions.h" #include "mozilla/mozalloc.h" #include "mozilla/mozalloc_oom.h" // for mozalloc_handle_oom @@ -214,8 +215,30 @@ moz_malloc_usable_size(void *ptr) #endif } -size_t moz_malloc_size_of(const void *ptr) +size_t +moz_malloc_size_of(const void *ptr) { return moz_malloc_usable_size((void *)ptr); } + +#if defined(MOZ_MEMORY) +#include "mozjemalloc_types.h" +// mozmemory.h declares jemalloc_ptr_info(), but including that header in this +// file is complicated. So we just redeclare it here instead, and include +// mozjemalloc_types.h for jemalloc_ptr_info_t. +MOZ_JEMALLOC_API void jemalloc_ptr_info(const void* ptr, + jemalloc_ptr_info_t* info); +#endif + +size_t +moz_malloc_enclosing_size_of(const void *ptr) +{ +#if defined(MOZ_MEMORY) + jemalloc_ptr_info_t info; + jemalloc_ptr_info(ptr, &info); + return jemalloc_ptr_is_live(&info) ? info.size : 0; +#else + return 0; +#endif +} #endif diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h index 18752a7987a0..c7af9f5e75fd 100644 --- a/memory/mozalloc/mozalloc.h +++ b/memory/mozalloc/mozalloc.h @@ -98,6 +98,12 @@ MFBT_API size_t moz_malloc_usable_size(void *ptr); MFBT_API size_t moz_malloc_size_of(const void *ptr); +/* + * Like moz_malloc_size_of(), but works reliably with interior pointers, i.e. + * pointers into the middle of a live allocation. + */ +MFBT_API size_t moz_malloc_enclosing_size_of(const void *ptr); + #if defined(HAVE_STRNDUP) MFBT_API char* moz_xstrndup(const char* str, size_t strsize) MOZ_ALLOCATOR; diff --git a/memory/mozjemalloc/mozjemalloc.cpp b/memory/mozjemalloc/mozjemalloc.cpp index b1529111e0f4..405b68d13512 100644 --- a/memory/mozjemalloc/mozjemalloc.cpp +++ b/memory/mozjemalloc/mozjemalloc.cpp @@ -1460,6 +1460,31 @@ extent_ad_comp(extent_node_t *a, extent_node_t *b) rb_wrap(static, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, extent_ad_comp) +static inline int +extent_bounds_comp(extent_node_t* aKey, extent_node_t* aNode) +{ + uintptr_t key_addr = (uintptr_t)aKey->addr; + uintptr_t node_addr = (uintptr_t)aNode->addr; + size_t node_size = aNode->size; + + // Is aKey within aNode? + if (node_addr <= key_addr && key_addr < node_addr + node_size) { + return 0; + } + + return ((key_addr > node_addr) - (key_addr < node_addr)); +} + +/* + * This is an expansion of just the search function from the rb_wrap macro. + */ +static extent_node_t * +extent_tree_bounds_search(extent_tree_t *tree, extent_node_t *key) { + extent_node_t *ret; + rb_search(extent_node_t, link_ad, extent_bounds_comp, tree, key, ret); + return ret; +} + /* * End extent tree code. */ @@ -3544,6 +3569,134 @@ isalloc(const void *ptr) return (ret); } +MOZ_JEMALLOC_API void +jemalloc_ptr_info_impl(const void* aPtr, jemalloc_ptr_info_t* aInfo) +{ + arena_chunk_t* chunk = (arena_chunk_t*)CHUNK_ADDR2BASE(aPtr); + + // Is the pointer null, or within one chunk's size of null? + if (!chunk) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + // Look for huge allocations before looking for |chunk| in chunk_rtree. + // This is necessary because |chunk| won't be in chunk_rtree if it's + // the second or subsequent chunk in a huge allocation. + extent_node_t* node; + extent_node_t key; + malloc_mutex_lock(&huge_mtx); + key.addr = const_cast(aPtr); + node = extent_tree_bounds_search(&huge, &key); + if (node) { + *aInfo = { TagLiveHuge, node->addr, node->size }; + } + malloc_mutex_unlock(&huge_mtx); + if (node) { + return; + } + + // It's not a huge allocation. Check if we have a known chunk. + if (!malloc_rtree_get(chunk_rtree, (uintptr_t)chunk)) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + MOZ_DIAGNOSTIC_ASSERT(chunk->arena->magic == ARENA_MAGIC); + + // Get the page number within the chunk. + size_t pageind = (((uintptr_t)aPtr - (uintptr_t)chunk) >> pagesize_2pow); + if (pageind < arena_chunk_header_npages) { + // Within the chunk header. + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + size_t mapbits = chunk->map[pageind].bits; + + if (!(mapbits & CHUNK_MAP_ALLOCATED)) { + PtrInfoTag tag = TagFreedPageDirty; + if (mapbits & CHUNK_MAP_DIRTY) + tag = TagFreedPageDirty; + else if (mapbits & CHUNK_MAP_DECOMMITTED) + tag = TagFreedPageDecommitted; + else if (mapbits & CHUNK_MAP_MADVISED) + tag = TagFreedPageMadvised; + else if (mapbits & CHUNK_MAP_ZEROED) + tag = TagFreedPageZeroed; + else + MOZ_CRASH(); + + void* pageaddr = (void*)(uintptr_t(aPtr) & ~pagesize_mask); + *aInfo = { tag, pageaddr, pagesize }; + return; + } + + if (mapbits & CHUNK_MAP_LARGE) { + // It's a large allocation. Only the first page of a large + // allocation contains its size, so if the address is not in + // the first page, scan back to find the allocation size. + size_t size; + while (true) { + size = mapbits & ~pagesize_mask; + if (size != 0) { + break; + } + + // The following two return paths shouldn't occur in + // practice unless there is heap corruption. + + pageind--; + MOZ_DIAGNOSTIC_ASSERT(pageind >= arena_chunk_header_npages); + if (pageind < arena_chunk_header_npages) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + mapbits = chunk->map[pageind].bits; + MOZ_DIAGNOSTIC_ASSERT(mapbits & CHUNK_MAP_LARGE); + if (!(mapbits & CHUNK_MAP_LARGE)) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + } + + void* addr = ((char*)chunk) + (pageind << pagesize_2pow); + *aInfo = { TagLiveLarge, addr, size }; + return; + } + + // It must be a small allocation. + + auto run = (arena_run_t *)(mapbits & ~pagesize_mask); + MOZ_DIAGNOSTIC_ASSERT(run->magic == ARENA_RUN_MAGIC); + + // The allocation size is stored in the run metadata. + size_t size = run->bin->reg_size; + + // Address of the first possible pointer in the run after its headers. + uintptr_t reg0_addr = (uintptr_t)run + run->bin->reg0_offset; + if (aPtr < (void*)reg0_addr) { + // In the run header. + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + // Position in the run. + unsigned regind = ((uintptr_t)aPtr - reg0_addr) / size; + + // Pointer to the allocation's base address. + void* addr = (void*)(reg0_addr + regind * size); + + // Check if the allocation has been freed. + unsigned elm = regind >> (SIZEOF_INT_2POW + 3); + unsigned bit = regind - (elm << (SIZEOF_INT_2POW + 3)); + PtrInfoTag tag = ((run->regs_mask[elm] & (1U << bit))) + ? TagFreedSmall : TagLiveSmall; + + *aInfo = { tag, addr, size}; +} + static inline void arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, arena_chunk_map_t *mapelm) @@ -4772,6 +4925,7 @@ jemalloc_stats_impl(jemalloc_stats_t *stats) stats->small_max = small_max; stats->large_max = arena_maxclass; stats->chunksize = chunksize; + stats->page_size = pagesize; stats->dirty_max = opt_dirty_max; /* diff --git a/memory/mozjemalloc/mozjemalloc_types.h b/memory/mozjemalloc/mozjemalloc_types.h index 76092f2d40e6..9b22045bc3b8 100644 --- a/memory/mozjemalloc/mozjemalloc_types.h +++ b/memory/mozjemalloc/mozjemalloc_types.h @@ -60,6 +60,7 @@ typedef struct { size_t small_max; /* Max quantum-spaced allocation size. */ size_t large_max; /* Max sub-chunksize allocation size. */ size_t chunksize; /* Size of each virtual memory mapping. */ + size_t page_size; /* Size of pages. */ size_t dirty_max; /* Max dirty pages per arena. */ /* @@ -77,6 +78,70 @@ typedef struct { size_t bin_unused; /* Bytes committed to a bin but currently unused. */ } jemalloc_stats_t; +enum PtrInfoTag { + // The pointer is not currently known to the allocator. + // 'addr' and 'size' are always 0. + TagUnknown, + + // The pointer is within a live allocation. + // 'addr' and 'size' describe the allocation. + TagLiveSmall, + TagLiveLarge, + TagLiveHuge, + + // The pointer is within a small freed allocation. + // 'addr' and 'size' describe the allocation. + TagFreedSmall, + + // The pointer is within a freed page. Details about the original + // allocation, including its size, are not available. + // 'addr' and 'size' describe the page. + TagFreedPageDirty, + TagFreedPageDecommitted, + TagFreedPageMadvised, + TagFreedPageZeroed, +}; + +/* + * The information in jemalloc_ptr_info_t could be represented in a variety of + * ways. The chosen representation has the following properties. + * - The number of fields is minimized. + * - The 'tag' field unambiguously defines the meaning of the subsequent fields. + * Helper functions are used to group together related categories of tags. + */ +typedef struct { + enum PtrInfoTag tag; + void* addr; // meaning depends on tag; see above + size_t size; // meaning depends on tag; see above +} jemalloc_ptr_info_t; + +static inline bool +jemalloc_ptr_is_live(jemalloc_ptr_info_t* info) +{ + return info->tag == TagLiveSmall || + info->tag == TagLiveLarge || + info->tag == TagLiveHuge; +} + +static inline bool +jemalloc_ptr_is_freed(jemalloc_ptr_info_t* info) +{ + return info->tag == TagFreedSmall || + info->tag == TagFreedPageDirty || + info->tag == TagFreedPageDecommitted || + info->tag == TagFreedPageMadvised || + info->tag == TagFreedPageZeroed; +} + +static inline bool +jemalloc_ptr_is_freed_page(jemalloc_ptr_info_t* info) +{ + return info->tag == TagFreedPageDirty || + info->tag == TagFreedPageDecommitted || + info->tag == TagFreedPageMadvised || + info->tag == TagFreedPageZeroed; +} + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/memory/replace/replace/ReplaceMalloc.cpp b/memory/replace/replace/ReplaceMalloc.cpp index 9b651fc48247..7760c870250c 100644 --- a/memory/replace/replace/ReplaceMalloc.cpp +++ b/memory/replace/replace/ReplaceMalloc.cpp @@ -261,3 +261,14 @@ replace_jemalloc_thread_local_arena(bool aEnabled) hook_table->jemalloc_thread_local_arena_hook(aEnabled); } } + +void +replace_jemalloc_ptr_info(const void* aPtr, jemalloc_ptr_info_t* aInfo) +{ + gFuncs->jemalloc_ptr_info(aPtr, aInfo); + const malloc_hook_table_t* hook_table = gHookTable; + if (hook_table && hook_table->jemalloc_ptr_info_hook) { + hook_table->jemalloc_ptr_info_hook(aPtr, aInfo); + } +} + diff --git a/mfbt/Result.h b/mfbt/Result.h index 09c64b7ba5bb..8aec3ca2c953 100644 --- a/mfbt/Result.h +++ b/mfbt/Result.h @@ -95,6 +95,32 @@ public: E& unwrapErr() const { return *mErrorValue; } }; +/** + * Specialization for when the success type is Ok (or another empty class) and + * the error type is a value type which can never have the value 0 (as + * determined by UnusedZero<>). + */ +template +class ResultImplementation +{ + static constexpr E NullValue = E(0); + + E mErrorValue; + +public: + explicit ResultImplementation(V) : mErrorValue(NullValue) {} + explicit ResultImplementation(E aErrorValue) : mErrorValue(aErrorValue) + { + MOZ_ASSERT(aErrorValue != NullValue); + } + + bool isOk() const { return mErrorValue == NullValue; } + + V unwrap() const { return V(); } + E unwrapErr() const { return mErrorValue; } +}; + + /** * Specialization for when alignment permits using the least significant bit as * a tag bit. @@ -234,6 +260,14 @@ struct IsResult> : TrueType { }; } // namespace detail +template +auto +ToResult(Result&& aValue) + -> decltype(Forward>(aValue)) +{ + return Forward>(aValue); +} + /** * Result represents the outcome of an operation that can either succeed * or fail. It contains either a success value of type V or an error value of @@ -416,7 +450,7 @@ Err(E&& aErrorValue) */ #define MOZ_TRY(expr) \ do { \ - auto mozTryTempResult_ = (expr); \ + auto mozTryTempResult_ = ::mozilla::ToResult(expr); \ if (mozTryTempResult_.isErr()) { \ return ::mozilla::Err(mozTryTempResult_.unwrapErr()); \ } \ diff --git a/mfbt/ResultExtensions.h b/mfbt/ResultExtensions.h new file mode 100644 index 000000000000..b47509b6a32a --- /dev/null +++ b/mfbt/ResultExtensions.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +/* Extensions to the Result type to enable simpler handling of XPCOM/NSPR results. */ + +#ifndef mozilla_ResultExtensions_h +#define mozilla_ResultExtensions_h + +#include "mozilla/Assertions.h" +#include "nscore.h" +#include "prtypes.h" + +namespace mozilla { + +// Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY +// can be used in XPCOM methods with Result results. +template <> +class MOZ_MUST_USE_TYPE GenericErrorResult +{ + nsresult mErrorValue; + + template friend class Result; + +public: + explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) + { + MOZ_ASSERT(NS_FAILED(aErrorValue)); + } + + operator nsresult() { return mErrorValue; } +}; + +// Allow MOZ_TRY to handle `PRStatus` values. +inline Result ToResult(PRStatus aValue); + +} // namespace mozilla + +#include "mozilla/Result.h" + +namespace mozilla { + +inline Result +ToResult(nsresult aValue) +{ + if (NS_FAILED(aValue)) { + return Err(aValue); + } + return Ok(); +} + +inline Result +ToResult(PRStatus aValue) +{ + if (aValue == PR_SUCCESS) { + return Ok(); + } + return Err(NS_ERROR_FAILURE); +} + +} // namespace mozilla + +#endif // mozilla_ResultExtensions_h diff --git a/mfbt/Types.h b/mfbt/Types.h index e7e18abb2746..30f4ea3d1f7c 100644 --- a/mfbt/Types.h +++ b/mfbt/Types.h @@ -78,23 +78,28 @@ * export mfbt declarations when building mfbt, and they expose import mfbt * declarations when using mfbt. */ -#if defined(IMPL_MFBT) +#if defined(IMPL_MFBT) || (defined(JS_STANDALONE) && !defined(MOZ_MEMORY) && (defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API))) # define MFBT_API MOZ_EXPORT # define MFBT_DATA MOZ_EXPORT #else - /* - * On linux mozglue is linked in the program and we link libxul.so with - * -z,defs. Normally that causes the linker to reject undefined references in - * libxul.so, but as a loophole it allows undefined references to weak - * symbols. We add the weak attribute to the import version of the MFBT API - * macros to exploit this. - */ -# if defined(MOZ_GLUE_IN_PROGRAM) -# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API -# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA +# if defined(JS_STANDALONE) && !defined(MOZ_MEMORY) && defined(STATIC_JS_API) +# define MFBT_API +# define MFBT_DATA # else -# define MFBT_API MOZ_IMPORT_API -# define MFBT_DATA MOZ_IMPORT_DATA + /* + * On linux mozglue is linked in the program and we link libxul.so with + * -z,defs. Normally that causes the linker to reject undefined references in + * libxul.so, but as a loophole it allows undefined references to weak + * symbols. We add the weak attribute to the import version of the MFBT API + * macros to exploit this. + */ +# if defined(MOZ_GLUE_IN_PROGRAM) +# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API +# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA +# else +# define MFBT_API MOZ_IMPORT_API +# define MFBT_DATA MOZ_IMPORT_DATA +# endif # endif #endif diff --git a/mfbt/moz.build b/mfbt/moz.build index 3d8812228e2e..276870a7e589 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -76,6 +76,7 @@ EXPORTS.mozilla = [ 'RefCountType.h', 'RefPtr.h', 'Result.h', + 'ResultExtensions.h', 'ReverseIterator.h', 'RollingMean.h', 'Saturate.h', diff --git a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java index 11082bb1a33b..2d2cadd09bd4 100644 --- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java +++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java @@ -5,6 +5,7 @@ package org.mozilla.geckoview_example; +import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; @@ -106,14 +107,9 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { final AlertDialog.Builder builder = new AlertDialog.Builder(activity) .setTitle(title) .setMessage(msg) - .setPositiveButton(android.R.string.ok, /* onClickListener */ null) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.dismiss(); - } - }); - addCheckbox(builder, /* parent */ null, callback).show(); + .setPositiveButton(android.R.string.ok, /* onClickListener */ null); + createStandardDialog(addCheckbox(builder, /* parent */ null, callback), + callback).show(); } public void promptForButton(final GeckoView view, final String title, final String msg, @@ -126,13 +122,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } final AlertDialog.Builder builder = new AlertDialog.Builder(activity) .setTitle(title) - .setMessage(msg) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.dismiss(); - } - }); + .setMessage(msg); final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override @@ -157,18 +147,20 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { if (btnMsg[BUTTON_TYPE_NEGATIVE] != null) { builder.setNegativeButton(btnMsg[BUTTON_TYPE_NEGATIVE], listener); } - addCheckbox(builder, /* parent */ null, callback).show(); + createStandardDialog(addCheckbox(builder, /* parent */ null, callback), + callback).show(); } private int getViewPadding(final AlertDialog.Builder builder) { final TypedArray attr = builder.getContext().obtainStyledAttributes( new int[] { android.R.attr.listPreferredItemPaddingLeft }); - return attr.getDimensionPixelSize(0, 1); + final int padding = attr.getDimensionPixelSize(0, 1); + attr.recycle(); + return padding; } private LinearLayout addStandardLayout(final AlertDialog.Builder builder, - final String title, final String msg, - final AlertCallback callback) { + final String title, final String msg) { final ScrollView scrollView = new ScrollView(builder.getContext()); final LinearLayout container = new LinearLayout(builder.getContext()); final int horizontalPadding = getViewPadding(builder); @@ -179,14 +171,20 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { scrollView.addView(container); builder.setTitle(title) .setMessage(msg) - .setOnDismissListener(new DialogInterface.OnDismissListener() { + .setView(scrollView); + return container; + } + + private AlertDialog createStandardDialog(final AlertDialog.Builder builder, + final AlertCallback callback) { + final AlertDialog dialog = builder.create(); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(final DialogInterface dialog) { callback.dismiss(); } - }) - .setView(scrollView); - return container; + }); + return dialog; } public void promptForText(final GeckoView view, final String title, final String msg, @@ -198,7 +196,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final LinearLayout container = addStandardLayout(builder, title, msg, callback); + final LinearLayout container = addStandardLayout(builder, title, msg); final EditText editText = new EditText(builder.getContext()); editText.setText(value); container.addView(editText); @@ -212,7 +210,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } }); - addCheckbox(builder, container, callback).show(); + createStandardDialog(addCheckbox(builder, container, callback), callback).show(); } public void promptForAuth(final GeckoView view, final String title, final String msg, @@ -224,7 +222,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final LinearLayout container = addStandardLayout(builder, title, msg, callback); + final LinearLayout container = addStandardLayout(builder, title, msg); final int flags = options.getInt("flags"); final int level = options.getInt("level"); @@ -264,7 +262,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } } }); - addCheckbox(builder, container, callback).show(); + createStandardDialog(addCheckbox(builder, container, callback), callback).show(); } private void addChoiceItems(final int type, final ArrayAdapter list, @@ -303,7 +301,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - addStandardLayout(builder, title, msg, callback); + addStandardLayout(builder, title, msg); final ListView list = new ListView(builder.getContext()); if (type == CHOICE_TYPE_MULTIPLE) { @@ -413,7 +411,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { final AlertDialog dialog; if (type == CHOICE_TYPE_SINGLE || type == CHOICE_TYPE_MENU) { - dialog = builder.create(); + dialog = createStandardDialog(builder, callback); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView parent, final View v, @@ -460,7 +458,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { callback.confirm(items.toArray(new String[items.size()])); } }); - dialog = builder.create(); + dialog = createStandardDialog(builder, callback); } else { throw new UnsupportedOperationException(); } @@ -484,7 +482,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - addStandardLayout(builder, title, /* msg */ null, callback); + addStandardLayout(builder, title, /* msg */ null); final int initial = parseColor(value, /* def */ 0); final ArrayAdapter adapter = new ArrayAdapter( @@ -537,7 +535,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { list.setAdapter(adapter); builder.setView(list); - final AlertDialog dialog = builder.create(); + final AlertDialog dialog = createStandardDialog(builder, callback); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView parent, final View v, @@ -665,8 +663,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { timePicker = null; } - final LinearLayout container = addStandardLayout(builder, title, - /* msg */ null, callback); + final LinearLayout container = addStandardLayout(builder, title, /* msg */ null); container.setPadding(/* left */ 0, /* top */ 0, /* right */ 0, /* bottom */ 0); if (datePicker != null) { container.addView(datePicker); @@ -696,10 +693,11 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { }; builder.setNegativeButton(android.R.string.cancel, /* listener */ null) .setNeutralButton(R.string.clear_field, listener) - .setPositiveButton(android.R.string.ok, listener) - .show(); + .setPositiveButton(android.R.string.ok, listener); + createStandardDialog(builder, callback).show(); } + @TargetApi(19) public void promptForFile(GeckoView view, String title, int type, String[] mimeTypes, FileCallback callback) { @@ -738,10 +736,10 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { (mimeSubtype != null ? mimeSubtype : "*")); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - if (type == FILE_TYPE_MULTIPLE) { + if (Build.VERSION.SDK_INT >= 18 && type == FILE_TYPE_MULTIPLE) { intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); } - if (mimeTypes.length > 0) { + if (Build.VERSION.SDK_INT >= 19 && mimeTypes.length > 0) { intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); } @@ -799,20 +797,22 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(title) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.reject(); - } - }) .setNegativeButton(android.R.string.cancel, /* onClickListener */ null) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { callback.grant(); } - }) - .show(); + }); + + final AlertDialog dialog = builder.create(); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(final DialogInterface dialog) { + callback.reject(); + } + }); + dialog.show(); } private Spinner addMediaSpinner(final Context context, final ViewGroup container, @@ -858,8 +858,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final LinearLayout container = addStandardLayout(builder, title, /* msg */ null, - /* callback */ null); + final LinearLayout container = addStandardLayout(builder, title, /* msg */ null); final Spinner videoSpinner; if (video != null) { @@ -875,13 +874,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { audioSpinner = null; } - builder.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.reject(); - } - }) - .setNegativeButton(android.R.string.cancel, /* listener */ null) + builder.setNegativeButton(android.R.string.cancel, /* listener */ null) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override @@ -892,7 +885,15 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { ? (GeckoBundle) audioSpinner.getSelectedItem() : null; callback.grant(video, audio); } - }) - .show(); + }); + + final AlertDialog dialog = builder.create(); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(final DialogInterface dialog) { + callback.reject(); + } + }); + dialog.show(); } } diff --git a/mobile/android/tests/browser/robocop/robocop_input.html b/mobile/android/tests/browser/robocop/robocop_input.html index f8f4bf317231..a11bb8719214 100644 --- a/mobile/android/tests/browser/robocop/robocop_input.html +++ b/mobile/android/tests/browser/robocop/robocop_input.html @@ -26,6 +26,11 @@ setTimeout(function() { designMode.contentDocument.designMode = "on" }, 0); } + // Add key listeners to trigger dummy key synthesis. + input.addEventListener("keydown", function() {}); + textArea.addEventListener("keydown", function() {}); + contentEditable.addEventListener("keydown", function() {}); + // An input that resets the editor on every input by resetting the value property. let resetting_input = document.getElementById("resetting-input"); resetting_input.addEventListener("input", function() { @@ -64,6 +69,27 @@ } } + let key_log; + function get_key_metastate(event) { + return (event.ctrlKey ? "C" : "c") + + (event.altKey ? "A" : "a") + + (event.shiftKey ? "S" : "s") + + (event.metaKey ? "M" : "m"); + } + + function log_key(event) { + switch (event.type) { + case "keydown": + case "keypress": + case "keyup": + key_log += `${event.type}:${event.key},${get_key_metastate(event)};`; + break; + default: + key_log += "unknown;"; + break; + } + } + function get_event_target() { var editor = getEditor(); var parent = SpecialPowers.unwrap(editor.rootElement.parentElement); @@ -182,6 +208,25 @@ target.removeEventListener("selectionchange", log_event); }, + start_key_log: function() { + // Reset the log + key_log = ""; + + let target = get_event_target(); + target.addEventListener("keydown", log_key); + target.addEventListener("keypress", log_key); + target.addEventListener("keyup", log_key); + }, + + end_key_log: function() { + java.asyncCall("setKeyLog", key_log); + + let target = get_event_target(); + target.removeEventListener("keydown", log_key); + target.removeEventListener("keypress", log_key); + target.removeEventListener("keyup", log_key); + }, + test_bug1123514: function() { document.activeElement.addEventListener("input", function() { // Only works on input and textarea. diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java index ce7bebecf308..f6846ade9f2b 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java @@ -26,6 +26,7 @@ public class testInputConnection extends JavascriptBridgeTest { private static final String INITIAL_TEXT = "foo"; private String mEventsLog; + private String mKeyLog; public void testInputConnection() throws InterruptedException { GeckoHelper.blockForReady(); @@ -35,6 +36,9 @@ public class testInputConnection extends JavascriptBridgeTest { // Enable "selectionchange" events for input/textarea. mActions.setPref("dom.select_events.enabled", true, /* flush */ false); mActions.setPref("dom.select_events.textcontrols.enabled", true, /* flush */ false); + // Enable dummy key synthesis. + mActions.setPref("intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition", + true, /* flush */ false); final String url = mStringHelper.ROBOCOP_INPUT_URL; NavigationHelper.enterAndLoadUrl(url); @@ -87,6 +91,14 @@ public class testInputConnection extends JavascriptBridgeTest { return mEventsLog; } + public void setKeyLog(final String log) { + mKeyLog = log; + } + + public String getKeyLog() { + return mKeyLog; + } + private class BasicInputConnectionTest extends InputConnectionTest { private final String mType; @@ -188,6 +200,34 @@ public class testInputConnection extends JavascriptBridgeTest { ic.deleteSurroundingText(6, 0); assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Test key synthesis. + getJS().syncCall("start_key_log"); + ic.setComposingText("f", 1); // Synthesizes dummy key. + assertTextAndSelectionAt("Can compose F key", ic, "f", 1); + ic.finishComposingText(); // Does not synthesize key. + assertTextAndSelectionAt("Can finish F key", ic, "f", 1); + ic.commitText("o", 1); // Synthesizes O key. + assertTextAndSelectionAt("Can commit O key", ic, "fo", 2); + ic.commitText("of", 1); // Synthesizes dummy key. + assertTextAndSelectionAt("Can commit non-key string", ic, "foof", 4); + + getJS().syncCall("end_key_log"); + if (mType.equals("designMode")) { + // designMode doesn't support dummy key synthesis. + fAssertEquals("Can synthesize keys", + "keydown:o,casm;keypress:o,casm;keyup:o,casm;", // O key + getKeyLog()); + } else { + fAssertEquals("Can synthesize keys", + "keydown:Unidentified,casm;keyup:Unidentified,casm;" + // Dummy + "keydown:o,casm;keypress:o,casm;keyup:o,casm;" + // O key + "keydown:Unidentified,casm;keyup:Unidentified,casm;", // Dummy + getKeyLog()); + } + + ic.deleteSurroundingText(4, 0); + assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Bug 1133802, duplication when setting the same composing text more than once. ic.setComposingText("foo", 1); assertTextAndSelectionAt("Can set the composing text", ic, "foo", 3); @@ -325,6 +365,20 @@ public class testInputConnection extends JavascriptBridgeTest { ic.deleteSurroundingText(0, 3); assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Bug 1387889 - Latin sharp S (U+00DF) triggers Alt+S shortcut + getJS().syncCall("start_key_log"); + ic.commitText("\u00df", 1); // Synthesizes "Latin sharp S" key without modifiers. + assertTextAndSelectionAt("Can commit Latin sharp S key", ic, "\u00df", 1); + + getJS().syncCall("end_key_log"); + fAssertEquals("Can synthesize sharp S key", + "keydown:\u00df,casm;keypress:\u00df,casm;keyup:\u00df,casm;", + getKeyLog()); + + ic.finishComposingText(); + ic.deleteSurroundingText(1, 0); + assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Make sure we don't leave behind stale events for the following test. processGeckoEvents(); processInputConnectionEvents(); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 7ccd0ec96ecd..ef5fc97823d1 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1052,10 +1052,13 @@ sticky_pref("devtools.debugger.prompt-connection", true); sticky_pref("devtools.chrome.enabled", false); // Disable remote debugging connections sticky_pref("devtools.debugger.remote-enabled", false); +// enable JS dump() function. +sticky_pref("browser.dom.window.dump.enabled", false); #else // In local builds, enable the browser toolbox by default sticky_pref("devtools.chrome.enabled", true); sticky_pref("devtools.debugger.remote-enabled", true); +sticky_pref("browser.dom.window.dump.enabled", true); #endif @@ -5178,9 +5181,6 @@ pref("layout.css.touch_action.enabled", true); // This only has an effect in DEBUG-builds. pref("layout.css.expensive-style-struct-assertions.enabled", false); -// enable JS dump() function. -pref("browser.dom.window.dump.enabled", false); - #if defined(MOZ_WIDGET_ANDROID) // Network Information API pref("dom.netinfo.enabled", true); diff --git a/mozglue/build/mozglue.def.in b/mozglue/build/mozglue.def.in index 19d4151f592c..dcbdc6d793e2 100644 --- a/mozglue/build/mozglue.def.in +++ b/mozglue/build/mozglue.def.in @@ -33,6 +33,8 @@ EXPORTS wcsdup=wrap_wcsdup _wcsdup=wrap_wcsdup jemalloc_stats + jemalloc_purge_freed_pages jemalloc_free_dirty_pages jemalloc_thread_local_arena + jemalloc_ptr_info #endif diff --git a/mozglue/build/replace_malloc.mk b/mozglue/build/replace_malloc.mk index 162937385562..000d621015ee 100644 --- a/mozglue/build/replace_malloc.mk +++ b/mozglue/build/replace_malloc.mk @@ -20,6 +20,7 @@ OS_LDFLAGS += \ -Wl,-U,_replace_jemalloc_purge_freed_pages \ -Wl,-U,_replace_jemalloc_free_dirty_pages \ -Wl,-U,_replace_jemalloc_thread_local_arena \ + -Wl,-U,_replace_jemalloc_ptr_info \ $(NULL) EXTRA_DEPS += $(topsrcdir)/mozglue/build/replace_malloc.mk diff --git a/netwerk/base/SimpleChannel.cpp b/netwerk/base/SimpleChannel.cpp index 00cbb34ceda8..2fa882e4c5fe 100644 --- a/netwerk/base/SimpleChannel.cpp +++ b/netwerk/base/SimpleChannel.cpp @@ -21,18 +21,6 @@ namespace mozilla { namespace net { -// Like MOZ_TRY, but returns the unwrapped error value rather than a -// GenericErrorResult on failure. -#define TRY_VAR(target, expr) \ - do { \ - auto result = (expr); \ - if (result.isErr()) { \ - return result.unwrapErr(); \ - } \ - (target) = result.unwrap(); \ - } while (0) - - class SimpleChannel : public nsBaseChannel { public: @@ -63,7 +51,7 @@ SimpleChannel::OpenContentStream(bool async, nsIInputStream **streamOut, nsIChan NS_ENSURE_TRUE(mCallbacks, NS_ERROR_UNEXPECTED); nsCOMPtr stream; - TRY_VAR(stream, mCallbacks->OpenContentStream(async, this)); + MOZ_TRY_VAR(stream, mCallbacks->OpenContentStream(async, this)); MOZ_ASSERT(stream); mCallbacks = nullptr; @@ -79,7 +67,7 @@ SimpleChannel::BeginAsyncRead(nsIStreamListener* listener, nsIRequest** request) NS_ENSURE_TRUE(mCallbacks, NS_ERROR_UNEXPECTED); nsCOMPtr req; - TRY_VAR(req, mCallbacks->StartAsyncRead(listener, this)); + MOZ_TRY_VAR(req, mCallbacks->StartAsyncRead(listener, this)); mCallbacks = nullptr; @@ -87,8 +75,6 @@ SimpleChannel::BeginAsyncRead(nsIStreamListener* listener, nsIRequest** request) return NS_OK; } -#undef TRY_VAR - class SimpleChannelChild final : public SimpleChannel , public nsIChildChannel , public PSimpleChannelChild diff --git a/netwerk/base/SimpleChannel.h b/netwerk/base/SimpleChannel.h index 84c405be88b9..b32e4b52c951 100644 --- a/netwerk/base/SimpleChannel.h +++ b/netwerk/base/SimpleChannel.h @@ -6,7 +6,7 @@ #ifndef SimpleChannel_h #define SimpleChannel_h -#include "mozilla/Result.h" +#include "mozilla/ResultExtensions.h" #include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" diff --git a/netwerk/base/nsChannelClassifier.cpp b/netwerk/base/nsChannelClassifier.cpp index 9592a12e96d8..b3267caecc2d 100644 --- a/netwerk/base/nsChannelClassifier.cpp +++ b/netwerk/base/nsChannelClassifier.cpp @@ -210,16 +210,23 @@ LowerPriorityHelper(nsIChannel* aChannel) nsCOMPtr cos(do_QueryInterface(aChannel)); if (cos) { - uint32_t cosFlags = 0; - cos->GetClassFlags(&cosFlags); - isBlockingResource = cosFlags & (nsIClassOfService::UrgentStart | - nsIClassOfService::Leader | - nsIClassOfService::Unblocked); + if (nsContentUtils::IsTailingEnabled()) { + uint32_t cosFlags = 0; + cos->GetClassFlags(&cosFlags); + isBlockingResource = cosFlags & (nsIClassOfService::UrgentStart | + nsIClassOfService::Leader | + nsIClassOfService::Unblocked); + + // Requests not allowed to be tailed are usually those with higher + // prioritization. That overweights being a tracker: don't throttle + // them when not in background. + if (!(cosFlags & nsIClassOfService::TailForbidden)) { + cos->AddClassFlags(nsIClassOfService::Throttleable); + } + } else { + // Yes, we even don't want to evaluate the isBlockingResource when tailing is off + // see bug 1395525. - // Requests not allowed to be tailed are usually those with higher - // prioritization. That overweights being a tracker: don't throttle - // them when not in background. - if (!(cosFlags & nsIClassOfService::TailForbidden)) { cos->AddClassFlags(nsIClassOfService::Throttleable); } } diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index a1e46b04b2c1..0a4981382d0f 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -1724,8 +1724,12 @@ nsHttpChannel::CallOnStartRequest() // We must keep the cache entry in case of partial request. // Concurrent access is the same, we need the entry in // OnStopRequest. - if (!mCachedContentIsPartial && !mConcurrentCacheAccess) + // We also need the cache entry when racing cache with network to find + // out what is the source of the data. + if (!mCachedContentIsPartial && !mConcurrentCacheAccess && + !(mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_FROM_CACHE)) { CloseCacheEntry(false); + } } if (!mCanceled) { @@ -7572,7 +7576,6 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st "We should not call OnStopRequest twice"); mListener->OnStopRequest(this, mListenerContext, status); mOnStopRequestCalled = true; - } RemoveAsNonTailRequest(); diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.cpp b/netwerk/protocol/res/ExtensionProtocolHandler.cpp index 133c1a7cfad2..8f0e1b616a65 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp +++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp @@ -15,6 +15,7 @@ #include "mozilla/ipc/URIUtils.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/RefPtr.h" +#include "mozilla/ResultExtensions.h" #include "FileDescriptor.h" #include "FileDescriptorFile.h" @@ -52,19 +53,6 @@ using OptionalIPCStream = mozilla::ipc::OptionalIPCStream; namespace mozilla { -template <> -class MOZ_MUST_USE_TYPE GenericErrorResult -{ - nsresult mErrorValue; - - template friend class Result; - -public: - explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {} - - operator nsresult() { return mErrorValue; } -}; - namespace net { using extensions::URLInfo; @@ -75,26 +63,6 @@ LazyLogModule gExtProtocolLog("ExtProtocol"); StaticRefPtr ExtensionProtocolHandler::sSingleton; -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - /** * Helper class used with SimpleChannel to asynchronously obtain an input * stream or file descriptor from the parent for a remote moz-extension load @@ -462,21 +430,21 @@ ExtensionProtocolHandler::SubstituteRemoteChannel(nsIURI* aURI, nsIChannel** aRetVal) { MOZ_ASSERT(IsNeckoChild()); - NS_TRY(aURI ? NS_OK : NS_ERROR_INVALID_ARG); - NS_TRY(aLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aURI ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); nsAutoCString unResolvedSpec; - NS_TRY(aURI->GetSpec(unResolvedSpec)); + MOZ_TRY(aURI->GetSpec(unResolvedSpec)); nsAutoCString resolvedSpec; - NS_TRY(ResolveURI(aURI, resolvedSpec)); + MOZ_TRY(ResolveURI(aURI, resolvedSpec)); // Use the target URI scheme to determine if this is a packed or unpacked // extension URI. For unpacked extensions, we'll request an input stream // from the parent. For a packed extension, we'll request a file descriptor // for the JAR file. nsAutoCString scheme; - NS_TRY(net_ExtractURLScheme(resolvedSpec, scheme)); + MOZ_TRY(net_ExtractURLScheme(resolvedSpec, scheme)); if (scheme.EqualsLiteral("file")) { // Unpacked extension @@ -524,21 +492,21 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI, nsresult rv; nsCOMPtr convService = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr uri; - NS_TRY(channel->GetURI(getter_AddRefs(uri))); + MOZ_TRY(channel->GetURI(getter_AddRefs(uri))); const char* kFromType = "application/vnd.mozilla.webext.unlocalized"; const char* kToType = "text/css"; nsCOMPtr converter; - NS_TRY(convService->AsyncConvertData(kFromType, kToType, listener, + MOZ_TRY(convService->AsyncConvertData(kFromType, kToType, listener, uri, getter_AddRefs(converter))); if (haveLoadInfo) { - NS_TRY(origChannel->AsyncOpen2(converter)); + MOZ_TRY(origChannel->AsyncOpen2(converter)); } else { - NS_TRY(origChannel->AsyncOpen(converter, nullptr)); + MOZ_TRY(origChannel->AsyncOpen(converter, nullptr)); } return RequestOrReason(origChannel); @@ -609,7 +577,7 @@ ExtensionProtocolHandler::DevRepoContains(nsIFile* aRequestedFile, // On the first invocation, set mDevRepo if (!mAlreadyCheckedDevRepo) { mAlreadyCheckedDevRepo = true; - NS_TRY(mozilla::GetRepoDir(getter_AddRefs(mDevRepo))); + MOZ_TRY(mozilla::GetRepoDir(getter_AddRefs(mDevRepo))); if (MOZ_LOG_TEST(gExtProtocolLog, LogLevel::Debug)) { nsAutoCString repoPath; Unused << mDevRepo->GetNativePath(repoPath); @@ -618,7 +586,7 @@ ExtensionProtocolHandler::DevRepoContains(nsIFile* aRequestedFile, } if (mDevRepo) { - NS_TRY(mDevRepo->Contains(aRequestedFile, aResult)); + MOZ_TRY(mDevRepo->Contains(aRequestedFile, aResult)); } return Ok(); @@ -638,7 +606,7 @@ ExtensionProtocolHandler::AppDirContains(nsIFile* aExtensionDir, // On the first invocation, set mAppDir if (!mAlreadyCheckedAppDir) { mAlreadyCheckedAppDir = true; - NS_TRY(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(mAppDir))); + MOZ_TRY(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(mAppDir))); if (MOZ_LOG_TEST(gExtProtocolLog, LogLevel::Debug)) { nsAutoCString appDirPath; Unused << mAppDir->GetNativePath(appDirPath); @@ -647,7 +615,7 @@ ExtensionProtocolHandler::AppDirContains(nsIFile* aExtensionDir, } if (mAppDir) { - NS_TRY(mAppDir->Contains(aExtensionDir, aResult)); + MOZ_TRY(mAppDir->Contains(aExtensionDir, aResult)); } return Ok(); @@ -673,8 +641,8 @@ Result, nsresult> ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) { MOZ_ASSERT(!IsNeckoChild()); - NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); - NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); *aTerminateSender = true; nsresult rv; @@ -701,21 +669,21 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) */ nsAutoCString host; - NS_TRY(aChildURI->GetAsciiHost(host)); + MOZ_TRY(aChildURI->GetAsciiHost(host)); // Lookup the directory this host string resolves to nsCOMPtr baseURI; - NS_TRY(GetSubstitution(host, getter_AddRefs(baseURI))); + MOZ_TRY(GetSubstitution(host, getter_AddRefs(baseURI))); // The result should be a file URL for the extension base dir nsCOMPtr fileURL = do_QueryInterface(baseURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr extensionDir; - NS_TRY(fileURL->GetFile(getter_AddRefs(extensionDir))); + MOZ_TRY(fileURL->GetFile(getter_AddRefs(extensionDir))); bool isDirectory = false; - NS_TRY(extensionDir->IsDirectory(&isDirectory)); + MOZ_TRY(extensionDir->IsDirectory(&isDirectory)); if (!isDirectory) { // The host should map to a directory for unpacked extensions return Err(NS_ERROR_FILE_NOT_DIRECTORY); @@ -726,38 +694,38 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) // file channel because we only request remote streams for unpacked // extension resource loads where the URI resolves to a file. nsAutoCString resolvedSpec; - NS_TRY(ResolveURI(aChildURI, resolvedSpec)); + MOZ_TRY(ResolveURI(aChildURI, resolvedSpec)); nsAutoCString resolvedScheme; - NS_TRY(net_ExtractURLScheme(resolvedSpec, resolvedScheme)); + MOZ_TRY(net_ExtractURLScheme(resolvedSpec, resolvedScheme)); if (!resolvedScheme.EqualsLiteral("file")) { return Err(NS_ERROR_UNEXPECTED); } nsCOMPtr ioService = do_GetIOService(&rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr resolvedURI; - NS_TRY(ioService->NewURI(resolvedSpec, - nullptr, - nullptr, - getter_AddRefs(resolvedURI))); + MOZ_TRY(ioService->NewURI(resolvedSpec, + nullptr, + nullptr, + getter_AddRefs(resolvedURI))); // We use the system principal to get a file channel for the request, // but only after we've checked (above) that the child URI is of // moz-extension scheme and that the URI host maps to a directory. nsCOMPtr channel; - NS_TRY(NS_NewChannel(getter_AddRefs(channel), - resolvedURI, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_OTHER)); + MOZ_TRY(NS_NewChannel(getter_AddRefs(channel), + resolvedURI, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + nsIContentPolicy::TYPE_OTHER)); nsCOMPtr fileChannel = do_QueryInterface(channel, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr requestedFile; - NS_TRY(fileChannel->GetFile(getter_AddRefs(requestedFile))); + MOZ_TRY(fileChannel->GetFile(getter_AddRefs(requestedFile))); /* * Make sure the file we resolved to is within the extension directory. @@ -765,8 +733,8 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) // Normalize paths for sane comparisons. nsIFile::Contains depends on // it for reliable subpath checks. - NS_TRY(extensionDir->Normalize()); - NS_TRY(requestedFile->Normalize()); + MOZ_TRY(extensionDir->Normalize()); + MOZ_TRY(requestedFile->Normalize()); #if defined(XP_WIN) if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(extensionDir) || !widget::WinUtils::ResolveJunctionPointsAndSymLinks(requestedFile)) { @@ -775,7 +743,7 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) #endif bool isResourceFromExtensionDir = false; - NS_TRY(extensionDir->Contains(requestedFile, &isResourceFromExtensionDir)); + MOZ_TRY(extensionDir->Contains(requestedFile, &isResourceFromExtensionDir)); if (!isResourceFromExtensionDir) { bool isAllowed = false; MOZ_TRY(AllowExternalResource(extensionDir, requestedFile, &isAllowed)); @@ -786,11 +754,11 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) } nsCOMPtr inputStream; - NS_TRY(NS_NewLocalFileInputStream(getter_AddRefs(inputStream), - requestedFile, - PR_RDONLY, - -1, - nsIFileInputStream::DEFER_OPEN)); + MOZ_TRY(NS_NewLocalFileInputStream(getter_AddRefs(inputStream), + requestedFile, + PR_RDONLY, + -1, + nsIFileInputStream::DEFER_OPEN)); return inputStream; } @@ -801,8 +769,8 @@ ExtensionProtocolHandler::NewFD(nsIURI* aChildURI, NeckoParent::GetExtensionFDResolver& aResolve) { MOZ_ASSERT(!IsNeckoChild()); - NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); - NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); *aTerminateSender = true; nsresult rv; @@ -819,24 +787,24 @@ ExtensionProtocolHandler::NewFD(nsIURI* aChildURI, *aTerminateSender = false; nsAutoCString host; - NS_TRY(aChildURI->GetAsciiHost(host)); + MOZ_TRY(aChildURI->GetAsciiHost(host)); // We expect the host string to map to a JAR file because the URI // should refer to a web accessible resource for an enabled extension. nsCOMPtr subURI; - NS_TRY(GetSubstitution(host, getter_AddRefs(subURI))); + MOZ_TRY(GetSubstitution(host, getter_AddRefs(subURI))); nsCOMPtr jarURI = do_QueryInterface(subURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr innerFileURI; - NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); + MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarFile; - NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + MOZ_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); if (!mFileOpenerThread) { mFileOpenerThread = @@ -851,7 +819,7 @@ ExtensionProtocolHandler::NewFD(nsIURI* aChildURI, mozilla::NewRunnableMethod("ExtensionJarFileOpener", fileOpener, &ExtensionJARFileOpener::OpenFile); - NS_TRY(mFileOpenerThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL)); + MOZ_TRY(mFileOpenerThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL)); return Ok(); } @@ -905,13 +873,13 @@ LogCacheCheck(const nsIJARChannel* aJarChannel, nsresult rv; nsCOMPtr innerFileURI; - NS_TRY(aJarURI->GetJARFile(getter_AddRefs(innerFileURI))); + MOZ_TRY(aJarURI->GetJARFile(getter_AddRefs(innerFileURI))); nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarFile; - NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + MOZ_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); nsAutoCString uriSpec, jarSpec; Unused << aJarURI->GetSpec(uriSpec); @@ -934,16 +902,16 @@ ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI, // Build a JAR URI for this jar:file:// URI and use it to extract the // inner file URI. nsCOMPtr uri; - NS_TRY(NS_NewURI(getter_AddRefs(uri), aResolvedSpec)); + MOZ_TRY(NS_NewURI(getter_AddRefs(uri), aResolvedSpec)); nsCOMPtr jarURI = do_QueryInterface(uri, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarChannel = do_QueryInterface(*aRetVal, &rv); - NS_TRY(rv); + MOZ_TRY(rv); bool isCached = false; - NS_TRY(jarChannel->EnsureCached(&isCached)); + MOZ_TRY(jarChannel->EnsureCached(&isCached)); if (MOZ_LOG_TEST(gExtProtocolLog, LogLevel::Debug)) { Unused << LogCacheCheck(jarChannel, jarURI, isCached); } @@ -954,13 +922,13 @@ ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI, streamGetter = new ExtensionStreamGetter(jarChannel.forget()); } else { nsCOMPtr innerFileURI; - NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); + MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarFile; - NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + MOZ_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); streamGetter = new ExtensionStreamGetter(aURI, aLoadinfo, @@ -972,7 +940,5 @@ ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI, return Ok(); } -#undef NS_TRY - } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.h b/netwerk/protocol/res/ExtensionProtocolHandler.h index 4797930ff668..636653f2294b 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.h +++ b/netwerk/protocol/res/ExtensionProtocolHandler.h @@ -8,6 +8,7 @@ #include "mozilla/net/NeckoParent.h" #include "mozilla/LazyIdleThread.h" +#include "mozilla/Result.h" #include "SubstitutingProtocolHandler.h" namespace mozilla { diff --git a/python/mozboot/mozboot/base.py b/python/mozboot/mozboot/base.py index 87e787984bb4..876084fc699a 100644 --- a/python/mozboot/mozboot/base.py +++ b/python/mozboot/mozboot/base.py @@ -261,6 +261,7 @@ class BaseBootstrapper(object): def install_tooltool_clang_package(self, state_dir, checkout_root, toolchain_job): mach_binary = os.path.join(checkout_root, 'mach') + mach_binary = os.path.abspath(mach_binary) if not os.path.exists(mach_binary): raise ValueError("mach not found at %s" % mach_binary) diff --git a/security/certverifier/tests/gtest/BTSerializationTest.cpp b/security/certverifier/tests/gtest/BTSerializationTest.cpp index 8214c82ac590..c692a85864f3 100644 --- a/security/certverifier/tests/gtest/BTSerializationTest.cpp +++ b/security/certverifier/tests/gtest/BTSerializationTest.cpp @@ -74,7 +74,7 @@ TEST_F(BTSerializationTest, FailsDecodingInclusionProofUnexpectedData) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingInvalidHashSize) @@ -83,7 +83,7 @@ TEST_F(BTSerializationTest, FailsDecodingInvalidHashSize) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingInvalidHash) @@ -92,7 +92,7 @@ TEST_F(BTSerializationTest, FailsDecodingInvalidHash) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingMissingLogId) @@ -101,7 +101,7 @@ TEST_F(BTSerializationTest, FailsDecodingMissingLogId) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingNullPathLength) @@ -110,7 +110,7 @@ TEST_F(BTSerializationTest, FailsDecodingNullPathLength) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingPathLengthTooSmall) @@ -119,7 +119,7 @@ TEST_F(BTSerializationTest, FailsDecodingPathLengthTooSmall) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingPathLengthTooLarge) @@ -128,7 +128,7 @@ TEST_F(BTSerializationTest, FailsDecodingPathLengthTooLarge) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingNullTreeSize) @@ -137,7 +137,7 @@ TEST_F(BTSerializationTest, FailsDecodingNullTreeSize) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingLeafIndexOutOfBounds) @@ -146,7 +146,7 @@ TEST_F(BTSerializationTest, FailsDecodingLeafIndexOutOfBounds) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingExtraData) @@ -155,6 +155,6 @@ TEST_F(BTSerializationTest, FailsDecodingExtraData) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } } } // namespace mozilla::ct diff --git a/security/certverifier/tests/gtest/CTSerializationTest.cpp b/security/certverifier/tests/gtest/CTSerializationTest.cpp index 357fc507c060..82bc6a238e3a 100644 --- a/security/certverifier/tests/gtest/CTSerializationTest.cpp +++ b/security/certverifier/tests/gtest/CTSerializationTest.cpp @@ -229,14 +229,14 @@ TEST_F(CTSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp) const uint8_t INVALID_VERSION_BYTES[] = { 0x02, 0x00 }; Input invalidVersionSctInput(INVALID_VERSION_BYTES); Reader invalidVersionSctReader(invalidVersionSctInput); - EXPECT_EQ(Result::ERROR_BAD_DER, + EXPECT_EQ(pkix::Result::ERROR_BAD_DER, DecodeSignedCertificateTimestamp(invalidVersionSctReader, sct)); // Valid version, invalid length (missing data) const uint8_t INVALID_LENGTH_BYTES[] = { 0x00, 0x0a, 0x0b, 0x0c }; Input invalidLengthSctInput(INVALID_LENGTH_BYTES); Reader invalidLengthSctReader(invalidLengthSctInput); - EXPECT_EQ(Result::ERROR_BAD_DER, + EXPECT_EQ(pkix::Result::ERROR_BAD_DER, DecodeSignedCertificateTimestamp(invalidLengthSctReader, sct)); } diff --git a/security/certverifier/tests/gtest/CTTestUtils.cpp b/security/certverifier/tests/gtest/CTTestUtils.cpp index a3c5077f4317..09eb40bf04d0 100644 --- a/security/certverifier/tests/gtest/CTTestUtils.cpp +++ b/security/certverifier/tests/gtest/CTTestUtils.cpp @@ -754,83 +754,83 @@ ExtractEmbeddedSCTList(const Buffer& cert, Buffer& result) class OCSPExtensionTrustDomain : public TrustDomain { public: - Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, - Input, TrustLevel&) override + pkix::Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, + Input, TrustLevel&) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result FindIssuer(Input, IssuerChecker&, Time) override + pkix::Result FindIssuer(Input, IssuerChecker&, Time) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, - const Input*, const Input*) override + pkix::Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, + const Input*, const Input*) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override + pkix::Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result DigestBuf(Input item, DigestAlgorithm digestAlg, - /*out*/ uint8_t* digestBuf, size_t digestBufLen) override + pkix::Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) override { return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); } - Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time) + pkix::Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override + pkix::Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, - Input subjectPublicKeyInfo) override + pkix::Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override { return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, nullptr); } - Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) + pkix::Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, - Input subjectPublicKeyInfo) override + pkix::Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override { return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, nullptr); } - Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) + pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result NetscapeStepUpMatchesServerAuth(Time, bool&) override + pkix::Result NetscapeStepUpMatchesServerAuth(Time, bool&) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } void NoteAuxiliaryExtension(AuxiliaryExtension extension, Input data) override @@ -864,10 +864,10 @@ ExtractSCTListFromOCSPResponse(Input cert, bool expired; OCSPExtensionTrustDomain trustDomain; - Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, - time, /*time*/ - 1000, /*maxLifetimeInDays*/ - encodedResponse, expired); + pkix::Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, + time, /*time*/ + 1000, /*maxLifetimeInDays*/ + encodedResponse, expired); ASSERT_EQ(Success, rv); result = Move(trustDomain.signedCertificateTimestamps); diff --git a/testing/marionette/element.js b/testing/marionette/element.js index 0ee217fd3613..f9236a3019ad 100644 --- a/testing/marionette/element.js +++ b/testing/marionette/element.js @@ -624,8 +624,7 @@ element.generateUUID = function() { * Determines if el is stale. * * A stale element is an element no longer attached to the DOM or which - * ownerDocument is not the same as {@link WindowProxy}'s - * document. + * node document is not the active document. * * @param {Element} el * DOM element to check for staleness. @@ -636,8 +635,11 @@ element.generateUUID = function() { element.isStale = function(el) { let doc = el.ownerDocument; let win = doc.defaultView; - let sameDoc = el.ownerDocument === win.document; - return !sameDoc || !el.isConnected; + + if (!win || el.ownerDocument !== win.document) { + return true; + } + return !el.isConnected; }; /** diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 0807700484ab..9911059153bb 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -436317,11 +436317,11 @@ "testharness" ], "content-security-policy/nonce-hiding/script-nonces-hidden-meta.tentative.html": [ - "15b9eee7b8a6c646ef704c4cc43dc328503383cd", + "a1de5c33148f7e202d13bc5c25d115a29ac81b3b", "testharness" ], "content-security-policy/nonce-hiding/script-nonces-hidden.tentative.html": [ - "418cd0188ee589563735316ba619dea547f1f467", + "c3265d6ead066201f712aa06beac162f365dd058", "testharness" ], "content-security-policy/nonce-hiding/script-nonces-hidden.tentative.html.headers": [ @@ -436329,11 +436329,11 @@ "support" ], "content-security-policy/nonce-hiding/svgscript-nonces-hidden-meta.tentative.html": [ - "fdb549d9c1c82890dc5443a69a574633a90c37a2", + "9ea075f30ba75a712f1fd7c05d413caaca0af67a", "testharness" ], "content-security-policy/nonce-hiding/svgscript-nonces-hidden.tentative.html": [ - "da72488109e91f1fa0a3f4b5ada3f078562bc8e0", + "7345b65ebfdecad898ce1d3acd6ee5353163a38d", "testharness" ], "content-security-policy/nonce-hiding/svgscript-nonces-hidden.tentative.html.headers": [ @@ -579073,7 +579073,7 @@ "testharness" ], "fetch/api/abort/general.js": [ - "980a50cf0be82bc7ffb2e7024739173273ae1263", + "74ca971f67c7f06ad9555a4cd877753da5718452", "support" ], "fetch/api/abort/serviceworker-intercepted.https.html": [ @@ -579333,7 +579333,7 @@ "testharness" ], "fetch/api/headers/headers-record.html": [ - "84652a79c8ff64826faff6151cf4a48450e706d1", + "25ab8a6a8988b529c7023ed50a62f0ac46e20b8e", "testharness" ], "fetch/api/headers/headers-structure.html": [ @@ -584109,7 +584109,7 @@ "support" ], "html/dom/elements-metadata.js": [ - "0583e1a4a95f902758b3fb30ca451ff06f4c237e", + "edf5b655f9a79d4a3599b5332fde6b211f3ce8eb", "support" ], "html/dom/elements-misc.js": [ @@ -619661,11 +619661,11 @@ "support" ], "service-workers/service-worker/resources/update-max-aged-worker-imported-script.py": [ - "2124eef9484a7ba1d38724b38d944bf1806fc579", + "6a70122d9af961de27dd9958f86a92c0d7ff7f7e", "support" ], "service-workers/service-worker/resources/update-max-aged-worker.py": [ - "6076f9ae7dd0c2e729ac9d3872daed0379555df8", + "dc5784aa563f93a2ebd8c57acddefe8c3dcc1c3f", "support" ], "service-workers/service-worker/resources/update-nocookie-worker.py": [ diff --git a/testing/web-platform/meta/css/css-text-3/i18n/css3-text-line-break-baspglwj-022.html.ini b/testing/web-platform/meta/css/css-text-3/i18n/css3-text-line-break-baspglwj-022.html.ini deleted file mode 100644 index fcd96597dfbc..000000000000 --- a/testing/web-platform/meta/css/css-text-3/i18n/css3-text-line-break-baspglwj-022.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-text-line-break-baspglwj-022.html] - type: testharness - [ ] - expected: FAIL - diff --git a/toolkit/components/alerts/test/mochitest.ini b/toolkit/components/alerts/test/mochitest.ini index 12e2a8704099..38c0e79ebf56 100644 --- a/toolkit/components/alerts/test/mochitest.ini +++ b/toolkit/components/alerts/test/mochitest.ini @@ -13,4 +13,4 @@ skip-if = toolkit == 'android' [test_image.html] [test_multiple_alerts.html] [test_principal.html] -skip-if = toolkit == 'android' +skip-if = toolkit == 'android' || (os == "win" && bits == 32) # Bug 1336638 diff --git a/toolkit/components/extensions/ExtensionCommon.jsm b/toolkit/components/extensions/ExtensionCommon.jsm index a68987658a93..edc1de9d0c45 100644 --- a/toolkit/components/extensions/ExtensionCommon.jsm +++ b/toolkit/components/extensions/ExtensionCommon.jsm @@ -917,6 +917,10 @@ class CanOfAPIs { * its appropriate event handler method to be called. Currently * only accepts "startup". * + * @property {Array} permissions + * An optional list of permissions, any of which must be present + * in order for the module to load. + * * @property {Array>} paths * A list of paths from the root API object which, when accessed, * will cause the API module to be instantiated and injected. @@ -1219,6 +1223,10 @@ class SchemaAPIManager extends EventEmitter { _checkGetAPI(name, extension, scope = null) { let module = this.modules.get(name); + if (module.permissions && !module.permissions.some(perm => extension.hasPermission(perm))) { + return false; + } + if (!scope) { return true; } diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index 6a2b0f20eec2..04c50c032bec 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -80,7 +80,9 @@ let apiManager = new class extends SchemaAPIManager { let promises = []; for (let apiName of this.eventModules.get("startup")) { promises.push(this.asyncGetAPI(apiName, extension).then(api => { - api.onStartup(extension.startupReason); + if (api) { + api.onStartup(extension.startupReason); + } })); } diff --git a/toolkit/components/extensions/WebExtensionPolicy.cpp b/toolkit/components/extensions/WebExtensionPolicy.cpp index b599d3cbb42c..8c8cc1db95ad 100644 --- a/toolkit/components/extensions/WebExtensionPolicy.cpp +++ b/toolkit/components/extensions/WebExtensionPolicy.cpp @@ -8,6 +8,7 @@ #include "mozilla/extensions/WebExtensionPolicy.h" #include "mozilla/AddonManagerWebAPI.h" +#include "mozilla/ResultExtensions.h" #include "nsEscape.h" #include "nsISubstitutingProtocolHandler.h" #include "nsNetUtil.h" @@ -18,26 +19,6 @@ namespace extensions { using namespace dom; -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - static const char kProto[] = "moz-extension"; static const char kBackgroundPageHTMLStart[] = "\n\ @@ -224,9 +205,9 @@ WebExtensionPolicy::GetURL(const nsAString& aPath) const nsPrintfCString spec("%s://%s/", kProto, mHostname.get()); nsCOMPtr uri; - NS_TRY(NS_NewURI(getter_AddRefs(uri), spec)); + MOZ_TRY(NS_NewURI(getter_AddRefs(uri), spec)); - NS_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec)); + MOZ_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec)); return NS_ConvertUTF8toUTF16(spec); } diff --git a/toolkit/components/extensions/ext-toolkit.json b/toolkit/components/extensions/ext-toolkit.json index 63af22e0d395..eac5e906a16f 100644 --- a/toolkit/components/extensions/ext-toolkit.json +++ b/toolkit/components/extensions/ext-toolkit.json @@ -29,6 +29,7 @@ "schema": "chrome://extensions/content/schemas/contextual_identities.json", "scopes": ["addon_parent"], "events": ["startup"], + "permissions": ["contextualIdentities"], "paths": [ ["contextualIdentities"] ] diff --git a/toolkit/components/extensions/test/xpcshell/head.js b/toolkit/components/extensions/test/xpcshell/head.js index 96bf15cc9476..7efab80051b6 100644 --- a/toolkit/components/extensions/test/xpcshell/head.js +++ b/toolkit/components/extensions/test/xpcshell/head.js @@ -10,22 +10,17 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Timer.jsm"); Components.utils.import("resource://testing-common/AddonTestUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ContentTask", - "resource://testing-common/ContentTask.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Extension", - "resource://gre/modules/Extension.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionData", - "resource://gre/modules/Extension.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionTestUtils", - "resource://testing-common/ExtensionXPCShellUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "HttpServer", - "resource://testing-common/httpd.js"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Schemas", - "resource://gre/modules/Schemas.jsm"); +XPCOMUtils.defineLazyModuleGetters(this, { + ContentTask: "resource://testing-common/ContentTask.jsm", + Extension: "resource://gre/modules/Extension.jsm", + ExtensionData: "resource://gre/modules/Extension.jsm", + ExtensionParent: "resource://gre/modules/ExtensionParent.jsm", + ExtensionTestUtils: "resource://testing-common/ExtensionXPCShellUtils.jsm", + FileUtils: "resource://gre/modules/FileUtils.jsm", + HttpServer: "resource://testing-common/httpd.js", + NetUtil: "resource://gre/modules/NetUtil.jsm", + Schemas: "resource://gre/modules/Schemas.jsm", +}); Services.prefs.setBoolPref("extensions.webextensions.remote", false); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js b/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js new file mode 100644 index 000000000000..29a9e4ff4dae --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js @@ -0,0 +1,57 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const STARTUP_APIS = [ + "backgroundPage", +]; + +const STARTUP_MODULES = [ + "resource://gre/modules/Extension.jsm", + "resource://gre/modules/ExtensionCommon.jsm", + "resource://gre/modules/ExtensionParent.jsm", + // FIXME: This is only loaded at startup for new extension installs. + // Otherwise the data comes from the startup cache. We should test for + // this. + "resource://gre/modules/ExtensionPermissions.jsm", + "resource://gre/modules/ExtensionUtils.jsm", +]; + +if (!Services.prefs.getBoolPref("extensions.webextensions.remote")) { + STARTUP_MODULES.push( + "resource://gre/modules/ExtensionChild.jsm", + "resource://gre/modules/ExtensionPageChild.jsm"); +} + +// Tests that only the minimal set of API scripts and modules are loaded at +// startup for a simple extension. +add_task(async function test_loaded_scripts() { + await ExtensionTestUtils.startAddonManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + background() {}, + manifest: {}, + }); + + await extension.startup(); + + const {apiManager} = ExtensionParent; + + const loadedAPIs = Array.from(apiManager.modules.values()) + .filter(m => m.loaded || m.asyncLoaded) + .map(m => m.namespaceName); + + deepEqual(loadedAPIs.sort(), STARTUP_APIS, + "No extra APIs should be loaded at startup for a simple extension"); + + + const loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader); + let loadedModules = loader.loadedModules() + .filter(url => url.startsWith("resource://gre/modules/Extension")); + + deepEqual(loadedModules.sort(), STARTUP_MODULES.sort(), + "No extra extension modules should be loaded at startup for a simple extension"); + + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini index a1c2f6a98ce1..441d057bc4f4 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini @@ -58,6 +58,7 @@ skip-if = true # This test no longer tests what it is meant to test. [test_ext_simple.js] [test_ext_startup_cache.js] skip-if = os == "android" +[test_ext_startup_perf.js] [test_ext_storage.js] [test_ext_storage_sync.js] head = head.js head_sync.js diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini index de8939274a80..e34ec0717691 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini @@ -1,5 +1,5 @@ [test_ext_i18n.js] -skip-if = os == "android" +skip-if = os == "android" || (os == "win" && debug) [test_ext_i18n_css.js] [test_ext_contentscript.js] [test_ext_contentscript_xrays.js] diff --git a/toolkit/components/places/FaviconHelpers.cpp b/toolkit/components/places/FaviconHelpers.cpp index 2adf9c41aa54..a9e186fd5ed1 100644 --- a/toolkit/components/places/FaviconHelpers.cpp +++ b/toolkit/components/places/FaviconHelpers.cpp @@ -594,10 +594,12 @@ AsyncFetchAndSetIconForPage::FetchFromNetwork() { priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST); } - nsCOMPtr cos = do_QueryInterface(channel); - if (cos) { - cos->AddClassFlags(nsIClassOfService::Tail | - nsIClassOfService::Throttleable); + if (nsContentUtils::IsTailingEnabled()) { + nsCOMPtr cos = do_QueryInterface(channel); + if (cos) { + cos->AddClassFlags(nsIClassOfService::Tail | + nsIClassOfService::Throttleable); + } } rv = channel->AsyncOpen2(this); diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index 909a15b39e7a..0a4b2befd305 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -1089,8 +1089,9 @@ Search.prototype = { // If we do not have enough results, and our match type is // MATCH_BOUNDARY_ANYWHERE, search again with MATCH_ANYWHERE to get more // results. + let count = this._counts[MATCHTYPE.GENERAL] + this._counts[MATCHTYPE.HEURISTIC]; if (this._matchBehavior == MATCH_BOUNDARY_ANYWHERE && - this._counts[MATCHTYPE.GENERAL] < Prefs.get("maxRichResults")) { + count < Prefs.get("maxRichResults")) { this._matchBehavior = MATCH_ANYWHERE; for (let [query, params] of [ this._adaptiveQuery, this._searchQuery ]) { @@ -1746,7 +1747,8 @@ Search.prototype = { this._addMatch(match); // If the search has been canceled by the user or by _addMatch, or we // fetched enough results, we can stop the underlying Sqlite query. - if (!this.pending || this._counts[MATCHTYPE.GENERAL] == Prefs.get("maxRichResults")) + let count = this._counts[MATCHTYPE.GENERAL] + this._counts[MATCHTYPE.HEURISTIC]; + if (!this.pending || count >= Prefs.get("maxRichResults")) cancel(); }, diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index daa6c179ab8f..1e9c8f7ddc6c 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -13612,16 +13612,5 @@ "n_values": 10, "bug_numbers": [1393392], "description": "The runtime status, sent when a remote HW video decoder is shutdown, of previously-blacklisted driver: 0:remote HW-decoding and GPU process didn't crash, 1:remote HW-decoding and GPU process crashed. The histogram is keyed by the blacklisted driver name." - }, - "FORM_FILLING_REQUIRED_TIME_MS": { - "record_in_processes": ["main"], - "alert_emails": ["autofill@lists.mozilla.org", "jcheng@mozilla.com", "chsiang@mozilla.com"], - "expires_in_version": "60", - "kind": "exponential", - "high": 300000, - "n_buckets": 22, - "keyed": true, - "bug_numbers": [1341569], - "description": "Milliseconds between starting to fill an autofill-eligible form field and submitting the form" } } diff --git a/toolkit/library/gtest/rust/Cargo.lock b/toolkit/library/gtest/rust/Cargo.lock index 3a0ffd040104..abefb9a8e594 100644 --- a/toolkit/library/gtest/rust/Cargo.lock +++ b/toolkit/library/gtest/rust/Cargo.lock @@ -1456,7 +1456,7 @@ dependencies = [ [[package]] name = "webrender" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1478,12 +1478,12 @@ dependencies = [ "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_api 0.49.0", + "webrender_api 0.50.0", ] [[package]] name = "webrender_api" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1507,8 +1507,8 @@ dependencies = [ "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.49.0", - "webrender_api 0.49.0", + "webrender 0.50.0", + "webrender_api 0.50.0", ] [[package]] diff --git a/toolkit/library/rust/Cargo.lock b/toolkit/library/rust/Cargo.lock index f7534e07b20c..0176fe38ebbf 100644 --- a/toolkit/library/rust/Cargo.lock +++ b/toolkit/library/rust/Cargo.lock @@ -1443,7 +1443,7 @@ dependencies = [ [[package]] name = "webrender" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1465,12 +1465,12 @@ dependencies = [ "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_api 0.49.0", + "webrender_api 0.50.0", ] [[package]] name = "webrender_api" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1494,8 +1494,8 @@ dependencies = [ "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.49.0", - "webrender_api 0.49.0", + "webrender 0.50.0", + "webrender_api 0.50.0", ] [[package]] diff --git a/toolkit/mozapps/extensions/AddonManagerStartup.cpp b/toolkit/mozapps/extensions/AddonManagerStartup.cpp index 49879e345fda..c31d4b9bdff4 100644 --- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp +++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp @@ -16,6 +16,7 @@ #include "mozilla/Compression.h" #include "mozilla/LinkedList.h" #include "mozilla/Preferences.h" +#include "mozilla/ResultExtensions.h" #include "mozilla/ScopeExit.h" #include "mozilla/Services.h" #include "mozilla/Unused.h" @@ -42,40 +43,6 @@ namespace mozilla { -template <> -class MOZ_MUST_USE_TYPE GenericErrorResult -{ - nsresult mErrorValue; - - template friend class Result; - -public: - explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {} - - operator nsresult() { return mErrorValue; } -}; - -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - - using Compression::LZ4; using dom::ipc::StructuredCloneData; @@ -143,7 +110,7 @@ ReadFile(nsIFile* file) nsCString result; AutoFDClose fd; - NS_TRY(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd.rwget())); + MOZ_TRY(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd.rwget())); auto size = PR_Seek64(fd, 0, PR_SEEK_END); PR_Seek64(fd, 0, PR_SEEK_SET); @@ -259,13 +226,13 @@ GetJarCache() NS_ENSURE_TRUE(ios, Err(NS_ERROR_FAILURE)); nsCOMPtr jarProto; - NS_TRY(ios->GetProtocolHandler("jar", getter_AddRefs(jarProto))); + MOZ_TRY(ios->GetProtocolHandler("jar", getter_AddRefs(jarProto))); nsCOMPtr jar = do_QueryInterface(jarProto); MOZ_ASSERT(jar); nsCOMPtr zipCache; - NS_TRY(jar->GetJARCache(getter_AddRefs(zipCache))); + MOZ_TRY(jar->GetJARCache(getter_AddRefs(zipCache))); return Move(zipCache); } @@ -278,22 +245,22 @@ GetFileLocation(nsIURI* uri) nsCOMPtr fileURL = do_QueryInterface(uri); nsCOMPtr file; if (fileURL) { - NS_TRY(fileURL->GetFile(getter_AddRefs(file))); + MOZ_TRY(fileURL->GetFile(getter_AddRefs(file))); location.Init(file); } else { nsCOMPtr jarURI = do_QueryInterface(uri); NS_ENSURE_TRUE(jarURI, Err(NS_ERROR_INVALID_ARG)); nsCOMPtr fileURI; - NS_TRY(jarURI->GetJARFile(getter_AddRefs(fileURI))); + MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(fileURI))); fileURL = do_QueryInterface(fileURI); NS_ENSURE_TRUE(fileURL, Err(NS_ERROR_INVALID_ARG)); - NS_TRY(fileURL->GetFile(getter_AddRefs(file))); + MOZ_TRY(fileURL->GetFile(getter_AddRefs(file))); nsCString entry; - NS_TRY(jarURI->GetJAREntry(entry)); + MOZ_TRY(jarURI->GetJAREntry(entry)); location.Init(file, entry.get()); } @@ -497,9 +464,9 @@ Addon::FullPath() } // If not an absolute path, fall back to a relative path from the location. - NS_TRY(NS_NewLocalFile(mLocation.Path(), false, getter_AddRefs(file))); + MOZ_TRY(NS_NewLocalFile(mLocation.Path(), false, getter_AddRefs(file))); - NS_TRY(file->AppendRelativePath(path)); + MOZ_TRY(file->AppendRelativePath(path)); return Move(file); } @@ -592,7 +559,7 @@ AddonManagerStartup::AddInstallLocation(Addon& addon) MOZ_TRY_VAR(file, addon.FullPath()); nsString path; - NS_TRY(file->GetPath(path)); + MOZ_TRY(file->GetPath(path)); auto type = addon.LocationType(); @@ -711,7 +678,7 @@ AddonManagerStartup::EncodeBlob(JS::HandleValue value, JSContext* cx, JS::Mutabl MOZ_TRY_VAR(lz4, EncodeLZ4(scData, STRUCTURED_CLONE_MAGIC)); JS::RootedObject obj(cx); - NS_TRY(nsContentUtils::CreateArrayBuffer(cx, lz4, &obj.get())); + MOZ_TRY(nsContentUtils::CreateArrayBuffer(cx, lz4, &obj.get())); result.set(JS::ObjectValue(*obj)); return NS_OK; @@ -761,16 +728,16 @@ AddonManagerStartup::EnumerateZipFile(nsIFile* file, const nsACString& pattern, MOZ_TRY_VAR(zipCache, GetJarCache()); nsCOMPtr zip; - NS_TRY(zipCache->GetZip(file, getter_AddRefs(zip))); + MOZ_TRY(zipCache->GetZip(file, getter_AddRefs(zip))); nsCOMPtr entries; - NS_TRY(zip->FindEntries(pattern, getter_AddRefs(entries))); + MOZ_TRY(zip->FindEntries(pattern, getter_AddRefs(entries))); nsTArray results; bool hasMore; while (NS_SUCCEEDED(entries->HasMore(&hasMore)) && hasMore) { nsAutoCString name; - NS_TRY(entries->GetNext(name)); + MOZ_TRY(entries->GetNext(name)); results.AppendElement(NS_ConvertUTF8toUTF16(name)); } diff --git a/toolkit/mozapps/update/nsIUpdateService.idl b/toolkit/mozapps/update/nsIUpdateService.idl index 2ae7efe1d1e1..6d1c7cded98a 100644 --- a/toolkit/mozapps/update/nsIUpdateService.idl +++ b/toolkit/mozapps/update/nsIUpdateService.idl @@ -49,6 +49,14 @@ interface nsIUpdatePatch : nsISupports */ attribute AString state; + /** + * A numeric error code that conveys additional information about the state of + * a failed update. If the update is not in the "failed" state the value is + * zero. The possible values are located in common/errors.h and values between + * 80 and 99 are in nsUpdateService.js. + */ + attribute long errorCode; + /** * true if this patch is currently selected as the patch to be downloaded and * installed for this update transaction, false if another patch from this @@ -183,13 +191,10 @@ interface nsIUpdate : nsISupports attribute AString state; /** - * A numeric error code that conveys additional information about the state - * of a failed update or failed certificate attribute check during an update - * check. If the update is not in the "failed" state or the certificate - * attribute check has not failed the value is zero. - * - * TODO: Define typical error codes (for now, see updater/errors.h and the - * CERT_ATTR_CHECK_FAILED_* values in nsUpdateService.js) + * A numeric error code that conveys additional information about the state of + * a failed update. If the update is not in the "failed" state the value is + * zero. The possible values are located in common/errors.h and values between + * 80 and 99 are in nsUpdateService.js. */ attribute long errorCode; diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 941a4870ec11..0ea3439ff32a 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -144,6 +144,9 @@ const SERVICE_ERRORS = [SERVICE_UPDATER_COULD_NOT_BE_STARTED, // Error codes 80 through 99 are reserved for nsUpdateService.js and are not // defined in common/errors.h +const ERR_OLDER_VERSION_OR_SAME_BUILD = 90; +const ERR_UPDATE_STATE_NONE = 91; +const ERR_CHANNEL_CHANGE = 92; const INVALID_UPDATER_STATE_CODE = 98; const INVALID_UPDATER_STATUS_CODE = 99; @@ -849,6 +852,8 @@ function cleanupActiveUpdate() { // Move the update from the Active Update list into the Past Updates list. var um = Cc["@mozilla.org/updates/update-manager;1"]. getService(Ci.nsIUpdateManager); + // Setting |activeUpdate| to null will move the active update to the update + // history. um.activeUpdate = null; um.saveUpdates(); @@ -1151,17 +1156,22 @@ UpdatePatch.prototype = { */ serialize: function UpdatePatch_serialize(updates) { var patch = updates.createElementNS(URI_UPDATE_NS, "patch"); - patch.setAttribute("type", this.type); - patch.setAttribute("URL", this.URL); + // Don't write an errorCode if it evaluates to false since 0 is the same as + // no error code. + if (this.errorCode) { + patch.setAttribute("errorCode", this.errorCode); + } // finalURL is not available until after the download has started if (this.finalURL) { patch.setAttribute("finalURL", this.finalURL); } - patch.setAttribute("size", this.size); if (this.selected) { patch.setAttribute("selected", this.selected); } + patch.setAttribute("size", this.size); patch.setAttribute("state", this.state); + patch.setAttribute("type", this.type); + patch.setAttribute("URL", this.URL); for (let p in this._properties) { if (this._properties[p].present) { @@ -1218,22 +1228,20 @@ UpdatePatch.prototype = { }, /** - * Returns whether or not the update.status file for this patch exists at the - * appropriate location. + * See nsIUpdateService.idl */ - get statusFileExists() { - var statusFile = getUpdatesDir(); - statusFile.append(FILE_UPDATE_STATUS); - return statusFile.exists(); + get errorCode() { + return this._properties.errorCode || 0; + }, + set errorCode(val) { + this._properties.errorCode = val; }, /** * See nsIUpdateService.idl */ get state() { - if (this._properties.state) - return this._properties.state; - return STATE_NONE; + return this._properties.state || STATE_NONE; }, set state(val) { this._properties.state = val; @@ -1307,6 +1315,11 @@ function Update(update) { if (val) { this.installDate = val; } + } else if (attr.name == "errorCode" && attr.value) { + let val = parseInt(attr.value); + if (val) { + this.errorCode = val; + } } else if (attr.name == "isCompleteUpdate") { this.isCompleteUpdate = attr.value == "true"; } else if (attr.name == "promptWaitTime") { @@ -1383,26 +1396,40 @@ Update.prototype = { * * We use a copy of the state cached on this object in |_state| only when * there is no selected patch, i.e. in the case when we could not load - * |.activeUpdate| from the update manager for some reason but still have + * |activeUpdate| from the update manager for some reason but still have * the update.status file to work with. */ _state: "", - set state(state) { - if (this.selectedPatch) - this.selectedPatch.state = state; - this._state = state; - return state; - }, get state() { if (this.selectedPatch) return this.selectedPatch.state; return this._state; }, + set state(state) { + if (this.selectedPatch) + this.selectedPatch.state = state; + this._state = state; + }, /** * See nsIUpdateService.idl + * + * We use a copy of the errorCode cached on this object in |_errorCode| only + * when there is no selected patch, i.e. in the case when we could not load + * |activeUpdate| from the update manager for some reason but still have + * the update.status file to work with. */ - errorCode: 0, + _errorCode: 0, + get errorCode() { + if (this.selectedPatch) + return this.selectedPatch.errorCode; + return this._errorCode; + }, + set errorCode(errorCode) { + if (this.selectedPatch) + this.selectedPatch.errorCode = errorCode; + this._errorCode = errorCode; + }, /** * See nsIUpdateService.idl @@ -1441,15 +1468,15 @@ Update.prototype = { } var update = updates.createElementNS(URI_UPDATE_NS, "update"); update.setAttribute("appVersion", this.appVersion); + update.setAttribute("backgroundInterval", this.backgroundInterval); update.setAttribute("buildID", this.buildID); update.setAttribute("channel", this.channel); update.setAttribute("displayVersion", this.displayVersion); update.setAttribute("installDate", this.installDate); update.setAttribute("isCompleteUpdate", this.isCompleteUpdate); update.setAttribute("name", this.name); - update.setAttribute("serviceURL", this.serviceURL); update.setAttribute("promptWaitTime", this.promptWaitTime); - update.setAttribute("backgroundInterval", this.backgroundInterval); + update.setAttribute("serviceURL", this.serviceURL); update.setAttribute("type", this.type); if (this.detailsURL) { @@ -1681,40 +1708,85 @@ UpdateService.prototype = { getService(Ci.nsIUpdateManager); var update = um.activeUpdate; var status = readStatusFile(getUpdatesDir()); - pingStateAndStatusCodes(update, true, status); - // STATE_NONE status typically means that the update.status file is present - // but a background download error occurred. if (status == STATE_NONE) { - LOG("UpdateService:_postUpdateProcessing - no status, no update"); + // A status of STATE_NONE in _postUpdateProcessing means that the + // update.status file is present but there isn't an update in progress so + // cleanup the update. + LOG("UpdateService:_postUpdateProcessing - status is none"); + if (!update) { + update = new Update(null); + } + update.state = STATE_FAILED; + update.errorCode = ERR_UPDATE_STATE_NONE; + update.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + let newStatus = STATE_FAILED + ": " + ERR_UPDATE_STATE_NONE; + pingStateAndStatusCodes(update, true, newStatus); cleanupActiveUpdate(); return; } - // Handle the case when the update is the same or older than the current - // version and nsUpdateDriver.cpp skipped updating due to the version being - // older than the current version. - if (update && update.appVersion && - (status == STATE_PENDING || status == STATE_PENDING_SERVICE || - status == STATE_APPLIED || status == STATE_APPLIED_SERVICE || - status == STATE_PENDING_ELEVATE)) { - if (Services.vc.compare(update.appVersion, Services.appinfo.version) < 0 || - Services.vc.compare(update.appVersion, Services.appinfo.version) == 0 && - update.buildID == Services.appinfo.appBuildID) { - LOG("UpdateService:_postUpdateProcessing - removing update for older " + - "or same application version"); + if (update && update.channel != UpdateUtils.UpdateChannel) { + LOG("UpdateService:_postUpdateProcessing - channel has changed, " + + "reloading default preferences to workaround bug 802022"); + // Workaround to get the distribution preferences loaded (Bug 774618). + // This can be removed after bug 802022 is fixed. Now that this code runs + // later during startup this code may no longer be necessary but it + // shouldn't be removed until after bug 802022 is fixed. + let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver); + prefSvc.observe(null, "reload-default-prefs", null); + if (update.channel != UpdateUtils.UpdateChannel) { + LOG("UpdateService:_postUpdateProcessing - update channel is " + + "different than application's channel, removing update. update " + + "channel: " + update.channel + ", expected channel: " + + UpdateUtils.UpdateChannel); + // User switched channels, clear out the old active update and remove + // partial downloads + update.state = STATE_FAILED; + update.errorCode = ERR_CHANNEL_CHANGE; + update.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + let newStatus = STATE_FAILED + ": " + ERR_CHANNEL_CHANGE; + pingStateAndStatusCodes(update, true, newStatus); cleanupActiveUpdate(); return; } } + // Handle the case when the update is the same or older than the current + // version and nsUpdateDriver.cpp skipped updating due to the version being + // older than the current version. This also handles the general case when + // an update is for an older version or the same version and same build ID. + if (update && update.appVersion && + (status == STATE_PENDING || status == STATE_PENDING_SERVICE || + status == STATE_APPLIED || status == STATE_APPLIED_SERVICE || + status == STATE_PENDING_ELEVATE || status == STATE_DOWNLOADING)) { + if (Services.vc.compare(update.appVersion, Services.appinfo.version) < 0 || + Services.vc.compare(update.appVersion, Services.appinfo.version) == 0 && + update.buildID == Services.appinfo.appBuildID) { + LOG("UpdateService:_postUpdateProcessing - removing update for older " + + "application version or same application version with same build " + + "ID. update application version: " + update.appVersion + ", " + + "application version: " + Services.appinfo.version + ", update " + + "build ID: " + update.buildID + ", application build ID: " + + Services.appinfo.appBuildID); + update.state = STATE_FAILED; + update.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + update.errorCode = ERR_OLDER_VERSION_OR_SAME_BUILD; + // This could be split out to report telemetry for each case. + let newStatus = STATE_FAILED + ": " + ERR_OLDER_VERSION_OR_SAME_BUILD; + pingStateAndStatusCodes(update, true, newStatus); + cleanupActiveUpdate(); + return; + } + } + + pingStateAndStatusCodes(update, true, status); if (status == STATE_DOWNLOADING) { LOG("UpdateService:_postUpdateProcessing - patch found in downloading " + "state"); - if (update && update.state != STATE_SUCCEEDED) { - // Resume download - status = this.downloadUpdate(update, true); - if (status == STATE_NONE) - cleanupActiveUpdate(); + // Resume download + status = this.downloadUpdate(update, true); + if (status == STATE_NONE) { + cleanupActiveUpdate(); } return; } @@ -1766,9 +1838,6 @@ UpdateService.prototype = { if (status != STATE_SUCCEEDED) { - // Since the update didn't succeed save a copy of the active update's - // current state to the updates.xml so it is possible to track failures. - um.saveUpdates(); // Rotate the update logs so the update log isn't removed. By passing // false the patch directory won't be removed. cleanUpUpdatesDir(false); @@ -1780,8 +1849,11 @@ UpdateService.prototype = { } update.statusText = gUpdateBundle.GetStringFromName("installSuccess"); - // Update the patch's metadata. - um.activeUpdate = update; + // The only time that update is not a reference to activeUpdate is when + // activeUpdate is null. + if (!um.activeUpdate) { + um.activeUpdate = update; + } // Done with this update. Clean it up. cleanupActiveUpdate(); @@ -2439,68 +2511,80 @@ UpdateService.prototype = { * @constructor */ function UpdateManager() { - // Ensure the Active Update file is loaded - var updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_ACTIVE_UPDATE_XML])); - if (updates.length > 0) { - // Under some edgecases such as Windows system restore the active-update.xml - // will contain a pending update without the status file which will return - // STATE_NONE. To recover from this situation clean the updates dir and - // rewrite the active-update.xml file without the broken update. + // Ensure the active update file is loaded and that the active update is set + // in the update manager if there is an existing active update. + let activeUpdates = this._loadXMLFileIntoArray(FILE_ACTIVE_UPDATE_XML); + if (activeUpdates.length > 0) { + this._activeUpdate = activeUpdates[0]; + // This check is performed here since UpdateService:_postUpdateProcessing + // won't be called when there isn't an update.status file. if (readStatusFile(getUpdatesDir()) == STATE_NONE) { + // Under some edgecases such as Windows system restore the + // active-update.xml will contain a pending update without the status + // file. To recover from this situation clean the updates dir and move + // the active update to the update history. + this._activeUpdate.state = STATE_FAILED; + this._activeUpdate.errorCode = ERR_UPDATE_STATE_NONE; + this._activeUpdate.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + let newStatus = STATE_FAILED + ": " + ERR_UPDATE_STATE_NONE; + pingStateAndStatusCodes(this._activeUpdate, true, newStatus); + // Setting |activeUpdate| to null will move the active update to the + // update history. + this.activeUpdate = null; + this.saveUpdates(); cleanUpUpdatesDir(); - this._writeUpdatesToXMLFile([], getUpdateFile([FILE_ACTIVE_UPDATE_XML])); - } else - this._activeUpdate = updates[0]; + } } } UpdateManager.prototype = { - /** - * All previously downloaded and installed updates, as an array of nsIUpdate - * objects. - */ - _updates: null, - /** * The current actively downloading/installing update, as a nsIUpdate object. */ _activeUpdate: null, /** - * Handle Observer Service notifications - * @param subject - * The subject of the notification - * @param topic - * The notification name - * @param data - * Additional data + * Whether the update history stored in _updates has changed since it was + * loaded. + */ + _updatesDirty: false, + + /** + * See nsIObserver.idl */ observe: function UM_observe(subject, topic, data) { // Hack to be able to run and cleanup tests by reloading the update data. if (topic == "um-reload-update-data") { - this._updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_UPDATES_XML])); this._activeUpdate = null; - var updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_ACTIVE_UPDATE_XML])); - if (updates.length > 0) - this._activeUpdate = updates[0]; + let activeUpdates = this._loadXMLFileIntoArray(FILE_ACTIVE_UPDATE_XML); + if (activeUpdates.length > 0) { + this._activeUpdate = activeUpdates[0]; + } + delete this._updates; + let updates = this._loadXMLFileIntoArray(FILE_UPDATES_XML); + Object.defineProperty(this, "_updates", { + value: updates, + writable: true, + configurable: true, + enumerable: true + }); } }, /** * Loads an updates.xml formatted file into an array of nsIUpdate items. - * @param file - * A nsIFile for the updates.xml file + * @param fileName + * The file name in the updates directory to load. * @return The array of nsIUpdate items held in the file. */ - _loadXMLFileIntoArray: function UM__loadXMLFileIntoArray(file) { + _loadXMLFileIntoArray: function UM__loadXMLFileIntoArray(fileName) { + let updates = []; + let file = getUpdateFile([fileName]); if (!file.exists()) { - LOG("UpdateManager:_loadXMLFileIntoArray: XML file does not exist"); - return []; + LOG("UpdateManager:_loadXMLFileIntoArray - XML file does not exist. " + + "path: " + file.path); + return updates; } - var result = []; var fileStream = Cc["@mozilla.org/network/file-input-stream;1"]. createInstance(Ci.nsIFileInputStream); fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0); @@ -2526,43 +2610,44 @@ UpdateManager.prototype = { LOG("UpdateManager:_loadXMLFileIntoArray - invalid update"); continue; } - result.push(update); + updates.push(update); } - } catch (e) { + } catch (ex) { LOG("UpdateManager:_loadXMLFileIntoArray - error constructing update " + - "list. Exception: " + e); + "list. Exception: " + ex); } fileStream.close(); - return result; + return updates; }, /** - * Load the update manager, initializing state from state files. + * Loads the update history from the updates.xml file and then replaces + * _updates with an array of all previously downloaded and installed updates + * so the file is only read once. */ - _ensureUpdates: function UM__ensureUpdates() { - if (!this._updates) { - this._updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_UPDATES_XML])); - var activeUpdates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_ACTIVE_UPDATE_XML])); - if (activeUpdates.length > 0) - this._activeUpdate = activeUpdates[0]; - } + get _updates() { + delete this._updates; + let updates = this._loadXMLFileIntoArray(FILE_UPDATES_XML); + Object.defineProperty(this, "_updates", { + value: updates, + writable: true, + configurable: true, + enumerable: true + }); + return this._updates; }, /** * See nsIUpdateService.idl */ - getUpdateAt: function UM_getUpdateAt(index) { - this._ensureUpdates(); - return this._updates[index]; + getUpdateAt: function UM_getUpdateAt(aIndex) { + return this._updates[aIndex]; }, /** * See nsIUpdateService.idl */ get updateCount() { - this._ensureUpdates(); return this._updates.length; }, @@ -2570,76 +2655,40 @@ UpdateManager.prototype = { * See nsIUpdateService.idl */ get activeUpdate() { - if (this._activeUpdate && - this._activeUpdate.channel != UpdateUtils.UpdateChannel) { - LOG("UpdateManager:get activeUpdate - channel has changed, " + - "reloading default preferences to workaround bug 802022"); - // Workaround to get distribution preferences loaded (Bug 774618). This - // can be removed after bug 802022 is fixed. - let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver); - prefSvc.observe(null, "reload-default-prefs", null); - if (this._activeUpdate.channel != UpdateUtils.UpdateChannel) { - // User switched channels, clear out any old active updates and remove - // partial downloads - this._activeUpdate = null; - this.saveUpdates(); - - // Destroy the updates directory, since we're done with it. - cleanUpUpdatesDir(); - } - } return this._activeUpdate; }, - set activeUpdate(activeUpdate) { - this._addUpdate(activeUpdate); - this._activeUpdate = activeUpdate; - if (!activeUpdate) { - // If |activeUpdate| is null, we have updated both lists - the active list - // and the history list, so we want to write both files. - this.saveUpdates(); - } else - this._writeUpdatesToXMLFile([this._activeUpdate], - getUpdateFile([FILE_ACTIVE_UPDATE_XML])); - return activeUpdate; - }, - - /** - * Add an update to the Updates list. If the item already exists in the list, - * replace the existing value with the new value. - * @param update - * The nsIUpdate object to add. - */ - _addUpdate: function UM__addUpdate(update) { - if (!update) - return; - this._ensureUpdates(); - // Only the latest update entry is checked so the the latest successful - // step for an update is recorded and all failures are kept. This way - // mutliple attempts to update for the same update are kept in the update - // history. - if (this._updates && - update.state != STATE_FAILED && - this._updates[0] && - this._updates[0].state != STATE_FAILED && - this._updates[0].appVersion == update.appVersion && - this._updates[0].buildID == update.buildID) { - // Replace the existing entry with the new value, updating - // all metadata. - this._updates[0] = update; - return; + set activeUpdate(aActiveUpdate) { + if (!aActiveUpdate && this._activeUpdate) { + this._updatesDirty = true; + // Add the current active update to the front of the update history. + this._updates.unshift(this._activeUpdate); + // Limit the update history to 10 updates. + this._updates.splice(10); } - // Otherwise add it to the front of the list. - this._updates.unshift(update); + + this._activeUpdate = aActiveUpdate; }, /** - * Serializes an array of updates to an XML file + * Serializes an array of updates to an XML file or removes the file if the + * array length is 0. * @param updates * An array of nsIUpdate objects - * @param file - * The nsIFile object to serialize to + * @param fileName + * The file name in the updates directory to write to. */ - _writeUpdatesToXMLFile: function UM__writeUpdatesToXMLFile(updates, file) { + _writeUpdatesToXMLFile: function UM__writeUpdatesToXMLFile(updates, fileName) { + let file = getUpdateFile([fileName]); + if (updates.length == 0) { + LOG("UpdateManager:_writeUpdatesToXMLFile - no updates to write. " + + "removing file: " + file.path); + try { + file.remove(false); + } catch (e) { + } + return; + } + var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); var modeFlags = FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | @@ -2656,11 +2705,7 @@ UpdateManager.prototype = { var doc = parser.parseFromString(EMPTY_UPDATES_DOCUMENT, "text/xml"); for (var i = 0; i < updates.length; ++i) { - // If appVersion isn't defined don't add the update. This happens when - // cleaning up invalid updates (e.g. incorrect channel). - if (updates[i] && updates[i].appVersion) { - doc.documentElement.appendChild(updates[i].serialize(doc)); - } + doc.documentElement.appendChild(updates[i].serialize(doc)); } var serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"]. @@ -2676,24 +2721,17 @@ UpdateManager.prototype = { * See nsIUpdateService.idl */ saveUpdates: function UM_saveUpdates() { - this._writeUpdatesToXMLFile([this._activeUpdate], - getUpdateFile([FILE_ACTIVE_UPDATE_XML])); - if (this._activeUpdate) - this._addUpdate(this._activeUpdate); - - this._ensureUpdates(); - // Don't write updates that don't have a state to the updates.xml file. - if (this._updates) { - let updates = this._updates.slice(); - for (let i = updates.length - 1; i >= 0; --i) { - let state = updates[i].state; - if (state == STATE_NONE) { - updates.splice(i, 1); - } - } - - this._writeUpdatesToXMLFile(updates.slice(0, 20), - getUpdateFile([FILE_UPDATES_XML])); + // The active update stored in the active-update.xml file will change during + // the lifetime of an active update and should always be updated when + // saveUpdates is called. + this._writeUpdatesToXMLFile(this._activeUpdate ? [this._activeUpdate] : [], + FILE_ACTIVE_UPDATE_XML); + // The update history stored in the updates.xml file should only need to be + // updated when an active update has been added to it in which case + // |_updatesDirty| will be true. + if (this._updatesDirty) { + this._updatesDirty = false; + this._writeUpdatesToXMLFile(this._updates.slice(0, 10), FILE_UPDATES_XML); } }, @@ -2712,11 +2750,6 @@ UpdateManager.prototype = { if (update.state == STATE_FAILED && parts[1]) { update.errorCode = parseInt(parts[1]); } - let um = Cc["@mozilla.org/updates/update-manager;1"]. - getService(Ci.nsIUpdateManager); - // Save a copy of the active update's current state to the updates.xml so - // it is possible to track failures. - um.saveUpdates(); // Rotate the update logs so the update log isn't removed if a complete // update is downloaded. By passing false the patch directory won't be @@ -2728,6 +2761,8 @@ UpdateManager.prototype = { handleFallbackToCompleteUpdate(update, true); } + // This can be removed after the update ui under update/content is + // removed. update.QueryInterface(Ci.nsIWritablePropertyBag); update.setProperty("stagingFailed", "true"); } @@ -2735,6 +2770,11 @@ UpdateManager.prototype = { writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SERVICE); } + // Now that the active update's properties have been updated write the + // active-update.xml to disk. Since there have been no changes to the update + // history the updates.xml will not be written to disk. + this.saveUpdates(); + // Send an observer notification which the app update doorhanger uses to // display a restart notification LOG("UpdateManager:refreshUpdateStatus - Notifying observers that " + @@ -3605,6 +3645,8 @@ Downloader.prototype = { getService(Ci.nsIUpdateManager); if (deleteActiveUpdate) { this._update.installDate = (new Date()).getTime(); + // Setting |activeUpdate| to null will move the active update to the + // update history. um.activeUpdate = null; } else if (um.activeUpdate) { um.activeUpdate.state = state; @@ -3664,13 +3706,11 @@ Downloader.prototype = { // downloading) and if at any point this was a foreground download // notify the user about the error. If the update was a background // update there is no notification since the user won't be expecting it. - if (!Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME)) { - this._update.QueryInterface(Ci.nsIWritablePropertyBag); - if (this._update.getProperty("foregroundDownload") == "true") { - let prompter = Cc["@mozilla.org/updates/update-prompt;1"]. - createInstance(Ci.nsIUpdatePrompt); - prompter.showUpdateError(this._update); - } + if (!Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME) && + this._update.getProperty("foregroundDownload") == "true") { + let prompter = Cc["@mozilla.org/updates/update-prompt;1"]. + createInstance(Ci.nsIUpdatePrompt); + prompter.showUpdateError(this._update); } // Prevent leaking the update object (bug 454964). diff --git a/toolkit/mozapps/update/tests/data/shared.js b/toolkit/mozapps/update/tests/data/shared.js index 490123f4ee61..92c7bfebd865 100644 --- a/toolkit/mozapps/update/tests/data/shared.js +++ b/toolkit/mozapps/update/tests/data/shared.js @@ -120,11 +120,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "gEnv", "@mozilla.org/process/environment;1", "nsIEnvironment"); -XPCOMUtils.defineLazyGetter(this, "gZipW", function test_gZipW() { - return Cc["@mozilla.org/zipwriter;1"]. - createInstance(Ci.nsIZipWriter); -}); - /* Triggers post-update processing */ function testPostUpdateProcessing() { gAUS.observe(null, "test-post-update-processing", ""); diff --git a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js index 604a8fc8fbb4..88e9519b4699 100644 --- a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js +++ b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js @@ -45,6 +45,12 @@ const INVALID_WORKING_DIR_PATH_ERROR = 76; const INVALID_CALLBACK_PATH_ERROR = 77; const INVALID_CALLBACK_DIR_ERROR = 78; +// Error codes 80 through 99 are reserved for nsUpdateService.js and are not +// defined in common/errors.h +const ERR_OLDER_VERSION_OR_SAME_BUILD = 90; +const ERR_UPDATE_STATE_NONE = 91; +const ERR_CHANNEL_CHANGE = 92; + const STATE_FAILED_DELIMETER = ": "; const STATE_FAILED_LOADSOURCE_ERROR_WRONG_SIZE = @@ -111,16 +117,16 @@ function getRemoteUpdatesXMLString(aUpdates) { */ function getRemoteUpdateString(aUpdateProps, aPatches) { const updateProps = { - type: "major", - name: "App Update Test", - displayVersion: null, appVersion: DEFAULT_UPDATE_VERSION, - buildID: "20080811053724", - detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", - promptWaitTime: null, backgroundInterval: null, + buildID: "20080811053724", custom1: null, - custom2: null + custom2: null, + detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", + displayVersion: null, + name: "App Update Test", + promptWaitTime: null, + type: "major" }; for (let name in aUpdateProps) { @@ -196,9 +202,6 @@ function getLocalUpdatesXMLString(aUpdates) { */ function getLocalUpdateString(aUpdateProps, aPatches) { const updateProps = { - type: "major", - name: "App Update Test", - displayVersion: null, _appVersion: null, get appVersion() { if (this._appVersion) { @@ -212,46 +215,49 @@ function getLocalUpdateString(aUpdateProps, aPatches) { set appVersion(val) { this._appVersion = val; }, - buildID: "20080811053724", - detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", - promptWaitTime: null, backgroundInterval: null, + buildID: "20080811053724", + channel: gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL), custom1: null, custom2: null, - serviceURL: "http://test_service/", - installDate: "1238441400314", - statusText: "Install Pending", - isCompleteUpdate: "true", - channel: gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL), + detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", + displayVersion: null, foregroundDownload: "true", - previousAppVersion: null + installDate: "1238441400314", + isCompleteUpdate: "true", + name: "App Update Test", + previousAppVersion: null, + promptWaitTime: null, + serviceURL: "http://test_service/", + statusText: "Install Pending", + type: "major" }; for (let name in aUpdateProps) { updateProps[name] = aUpdateProps[name]; } - let previousAppVersion = updateProps.previousAppVersion ? - "previousAppVersion=\"" + updateProps.previousAppVersion + "\" " : ""; - let serviceURL = "serviceURL=\"" + updateProps.serviceURL + "\" "; - let installDate = "installDate=\"" + updateProps.installDate + "\" "; - let statusText = updateProps.statusText ? - "statusText=\"" + updateProps.statusText + "\" " : ""; + let channel = "channel=\"" + updateProps.channel + "\" "; let isCompleteUpdate = "isCompleteUpdate=\"" + updateProps.isCompleteUpdate + "\" "; - let channel = "channel=\"" + updateProps.channel + "\" "; let foregroundDownload = updateProps.foregroundDownload ? - "foregroundDownload=\"" + updateProps.foregroundDownload + "\">" : ">"; + "foregroundDownload=\"" + updateProps.foregroundDownload + "\" " : ""; + let installDate = "installDate=\"" + updateProps.installDate + "\" "; + let previousAppVersion = updateProps.previousAppVersion ? + "previousAppVersion=\"" + updateProps.previousAppVersion + "\" " : ""; + let statusText = updateProps.statusText ? + "statusText=\"" + updateProps.statusText + "\" " : ""; + let serviceURL = "serviceURL=\"" + updateProps.serviceURL + "\">"; return getUpdateString(updateProps) + " " + - previousAppVersion + - serviceURL + - installDate + - statusText + - isCompleteUpdate + channel + + isCompleteUpdate + foregroundDownload + + installDate + + previousAppVersion + + statusText + + serviceURL + aPatches + ""; } @@ -310,16 +316,16 @@ function getUpdateString(aUpdateProps) { let custom2 = aUpdateProps.custom2 ? aUpdateProps.custom2 + " " : ""; let buildID = "buildID=\"" + aUpdateProps.buildID + "\""; - return " 0) { + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, aUpdateStatusState, msgTags[i] + + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, aUpdateErrCode, msgTags[i] + + "the first update errorCode" + MSG_SHOULD_EQUAL); + } + if (i != msgTags.length - 1) { + reloadUpdateManagerData(); + } + } +} + /** * On Mac OS X and Windows this checks if the post update '.running' file exists * to determine if the post update binary was launched. @@ -1968,11 +2013,6 @@ function checkUpdateStagedState(aUpdateState) { "the update state" + MSG_SHOULD_EQUAL); } - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_STAGE, - "the update state" + MSG_SHOULD_EQUAL); - let log = getUpdateLog(FILE_LAST_UPDATE_LOG); Assert.ok(log.exists(), MSG_SHOULD_EXIST + getMsgPath(log.path)); diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js index 0592ba597987..24cb2e1b23f8 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js @@ -28,19 +28,25 @@ function run_test() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager update count" + MSG_SHOULD_EQUAL); - let update = gUpdateManager.getUpdateAt(0); - Assert.equal(update.name, "Existing", - "the update's name" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - // Verify that the active-update.xml file has had the update from the old - // channel removed. - let file = getUpdatesXMLFile(true); - Assert.equal(readFile(file), getLocalUpdatesXMLString(""), - "the contents of active-update.xml" + MSG_SHOULD_EQUAL); + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, + "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_CHANNEL_CHANGE, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js index c59d5fe6b692..04619f109106 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js @@ -17,12 +17,33 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeStatusFile(STATE_DOWNLOADING); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_OLDER_VERSION_OR_SAME_BUILD, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js index a440ff8bad98..79ad55a52313 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js @@ -18,12 +18,33 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeStatusFile(STATE_DOWNLOADING); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_OLDER_VERSION_OR_SAME_BUILD, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js index ccffbb7a5750..2ce379cd97a8 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js @@ -15,8 +15,34 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeStatusFile(STATE_NONE); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + let updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); + Assert.ok(!gUpdateManager.activeUpdate, + "there should not be an active update"); + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, + "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_UPDATE_STATE_NONE, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); + let dir = getUpdatesDir(); dir.append(DIR_PATCH); Assert.ok(dir.exists(), MSG_SHOULD_EXIST); @@ -25,10 +51,5 @@ function run_test() { statusFile.append(FILE_UPDATE_STATUS); Assert.ok(!statusFile.exists(), MSG_SHOULD_NOT_EXIST); - Assert.ok(!gUpdateManager.activeUpdate, - "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, - "the update manager update count" + MSG_SHOULD_EQUAL); - doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js index d514c31e6cee..49a7de1b3bc2 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js @@ -15,14 +15,35 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeVersionFile("99.9"); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + let updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); // Check that there is no activeUpdate first so the updates directory is // cleaned up by the UpdateManager before the remaining tests. Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_UPDATE_STATE_NONE, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); let dir = getUpdatesDir(); dir.append(DIR_PATCH); diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js index 2b2f172d20d3..3cb4d86fceb3 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js @@ -18,7 +18,7 @@ function run_test() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, + Assert.equal(gUpdateManager.updateCount, 0, "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); Assert.equal(gUpdateManager.activeUpdate.state, STATE_DOWNLOADING, "the update manager activeUpdate state attribute" + diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js index f3f767394514..62a00b5a5183 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js @@ -31,6 +31,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + INVALID_CALLBACK_DIR_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js index 969f84f9df02..5357f43a8451 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js @@ -39,6 +39,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + INVALID_CALLBACK_PATH_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js index 70e03646a643..8099e6bfc573 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js @@ -41,6 +41,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js index 330578de64b0..0ac4fad1539b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js @@ -38,6 +38,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js index 8ddb34af0689..82d99a1d4cce 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_ERROR + : INVALID_APPLYTO_DIR_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js index c8ae3f0c650e..ea5752c98d64 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js @@ -37,6 +37,7 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js index e9b227657380..5c6369653c52 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR + : INVALID_APPLYTO_DIR_STAGED_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js index 87bbad4aa2b3..e1ba7834e59b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js index a550909b284e..aa27685ea1a1 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js @@ -31,6 +31,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js index b9f793236a7a..e00e59c8cad1 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js @@ -32,6 +32,7 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { + checkUpdateManager(STATE_AFTER_STAGE, true, STATE_AFTER_STAGE, 0, 0); removeUpdateInProgressLockFile(getAppBaseDir()); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js index aec663092261..51973e9bd27c 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js @@ -51,14 +51,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js index fa18b4a9b881..a2663f3faa5e 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js @@ -35,17 +35,9 @@ function stageUpdateFinished() { checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getStageDirFile, true); checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true); - // Change the active update to an older version to simulate installing a new - // version of the application while there is an update that has been staged. - let patchProps = {state: STATE_AFTER_STAGE}; - let patches = getLocalPatchString(patchProps); - let updateProps = {appVersion: "1.0"}; - let updates = getLocalUpdateString(updateProps, patches); - writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); // Change the version file to an older version to simulate installing a new // version of the application while there is an update that has been staged. - writeVersionFile("1.0"); - reloadUpdateManagerData(); + writeVersionFile("0.9"); // Try to switch the application to the staged application that was updated. runUpdateUsingApp(STATE_AFTER_STAGE); } @@ -54,15 +46,19 @@ function stageUpdateFinished() { * Called after the call to runUpdateUsingApp finishes. */ function runUpdateFinished() { + // Change the active update to an older version to simulate installing a new + // version of the application while there is an update that has been staged. + let patchProps = {state: STATE_AFTER_STAGE}; + let patches = getLocalPatchString(patchProps); + let updateProps = {appVersion: "0.9"}; + let updates = getLocalUpdateString(updateProps, patches); + getUpdatesXMLFile(true).remove(false); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); + reloadUpdateManagerData(); + standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_STAGE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + ERR_OLDER_VERSION_OR_SAME_BUILD, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile, !IS_MACOSX, false); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js index 5b9b08156d67..ca72310e3e2f 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js @@ -50,14 +50,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js index e76233fe6a74..ae61687ffa0c 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js @@ -33,14 +33,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js index b1505d58eb2a..de2bade3b4bc 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js @@ -53,14 +53,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js index a1cc7d043601..650e0ad5a4f5 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js @@ -66,14 +66,7 @@ function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); checkSymLinks(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js index 93333cadebd5..5f516fa763ff 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js index 79e54c1825a4..3d1342490020 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js index b1f84715f0bb..955bdfc3e1d7 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js index 85e92d290e1c..2dbb75c9faa1 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js index 1212c9ba2ee1..ab7aca8a5113 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js index 960c96f7b896..73aeb7b35be8 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js @@ -32,14 +32,8 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_FAILURE); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js index b39595f921d2..bfd20044969a 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js index 06d386ad63b0..e33bae3cc07d 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js index 89a2fff5ed07..ee32c967cb32 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js index ea85ddccc7a2..1788b2c026e0 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js index c5efaa8c0729..1a6563f55be3 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js index 4fdbadb5beb9..8c4605f2b872 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js index 4d12f4e42f3f..e09ea772296b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js index 5f64df34c997..6466b6938c7f 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js index d66f1f8117b1..d4dbd3d796d5 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js @@ -50,14 +50,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContains(ERR_PARENT_PID_PERSISTS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js index b83bafccc498..c364d99e9eaa 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js @@ -55,14 +55,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js index 39ea485cdcbb..ab40901b0bec 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js index a71bb8d49097..e009440ddc1b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js index 2cbe70ed8efd..8c3c7f4f854c 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js @@ -46,14 +46,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js index a9ce23420743..a6ad6601db1b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js @@ -30,10 +30,8 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js index f7745f68f41a..ae13c75e7dcd 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js @@ -53,14 +53,7 @@ function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); checkSymLinks(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js index ef15326de8d6..3b814ed526a0 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js @@ -52,14 +52,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js index 1008e867fc44..2a6df6cfe1fe 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js @@ -36,14 +36,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js index 616390f55cc7..2e0232136b02 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js @@ -36,14 +36,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js b/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js index 86a2eb821ff4..7033b1e3f1e8 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js @@ -34,16 +34,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, VERSION_DOWNGRADE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + VERSION_DOWNGRADE_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(STATE_FAILED_VERSION_DOWNGRADE_ERROR); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js b/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js index 6db906fbc570..fcee265f92da 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js @@ -34,16 +34,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, MAR_CHANNEL_MISMATCH_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + MAR_CHANNEL_MISMATCH_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(STATE_FAILED_MAR_CHANNEL_MISMATCH_ERROR); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js index 70e03646a643..8099e6bfc573 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js @@ -41,6 +41,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js index 330578de64b0..0ac4fad1539b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js @@ -38,6 +38,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js index 8ddb34af0689..82d99a1d4cce 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_ERROR + : INVALID_APPLYTO_DIR_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js index bbef6c7bd5d6..24146aeeac3c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js @@ -31,6 +31,7 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js index c8ae3f0c650e..ea5752c98d64 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js @@ -37,6 +37,7 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js index e9b227657380..5c6369653c52 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR + : INVALID_APPLYTO_DIR_STAGED_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js index 87bbad4aa2b3..e1ba7834e59b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js index a550909b284e..aa27685ea1a1 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js @@ -31,6 +31,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js index b9f793236a7a..e00e59c8cad1 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js @@ -32,6 +32,7 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { + checkUpdateManager(STATE_AFTER_STAGE, true, STATE_AFTER_STAGE, 0, 0); removeUpdateInProgressLockFile(getAppBaseDir()); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js index aec663092261..51973e9bd27c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js @@ -51,14 +51,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js index 5b9b08156d67..ca72310e3e2f 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js @@ -50,14 +50,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js index e76233fe6a74..ae61687ffa0c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js @@ -33,14 +33,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js index b1505d58eb2a..de2bade3b4bc 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js @@ -53,14 +53,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js index 93333cadebd5..5f516fa763ff 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js index 79e54c1825a4..3d1342490020 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js index b1f84715f0bb..955bdfc3e1d7 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js index 85e92d290e1c..2dbb75c9faa1 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js index 1212c9ba2ee1..ab7aca8a5113 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js index 960c96f7b896..73aeb7b35be8 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js @@ -32,14 +32,8 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_FAILURE); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js index b39595f921d2..bfd20044969a 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js index 06d386ad63b0..e33bae3cc07d 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js index 89a2fff5ed07..ee32c967cb32 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js index ea85ddccc7a2..1788b2c026e0 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js index c5efaa8c0729..1a6563f55be3 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js index 4fdbadb5beb9..8c4605f2b872 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js index 4d12f4e42f3f..e09ea772296b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js index 5f64df34c997..6466b6938c7f 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js index b83bafccc498..c364d99e9eaa 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js @@ -55,14 +55,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js index 39ea485cdcbb..ab40901b0bec 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js index a71bb8d49097..e009440ddc1b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js index 2cbe70ed8efd..8c3c7f4f854c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js @@ -46,14 +46,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js index a9ce23420743..a6ad6601db1b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js @@ -30,10 +30,8 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js index f7745f68f41a..ae13c75e7dcd 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js @@ -53,14 +53,7 @@ function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); checkSymLinks(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js index ef15326de8d6..3b814ed526a0 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js @@ -52,14 +52,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js index 1008e867fc44..2a6df6cfe1fe 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js @@ -36,14 +36,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js index 616390f55cc7..2e0232136b02 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js @@ -36,14 +36,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); diff --git a/uriloader/exthandler/tests/mochitest/blob.html b/uriloader/exthandler/tests/mochitest/blob.html new file mode 100644 index 000000000000..5960c50cde9a --- /dev/null +++ b/uriloader/exthandler/tests/mochitest/blob.html @@ -0,0 +1,17 @@ + + + +Test for Bug 13870787 + + + +download + + diff --git a/uriloader/exthandler/tests/mochitest/browser.ini b/uriloader/exthandler/tests/mochitest/browser.ini index a1de09e185d8..361d985e6e23 100644 --- a/uriloader/exthandler/tests/mochitest/browser.ini +++ b/uriloader/exthandler/tests/mochitest/browser.ini @@ -1,6 +1,7 @@ [DEFAULT] head = head.js support-files = + blob.html download_page.html download.bin protocolHandler.html @@ -8,5 +9,6 @@ support-files = [browser_auto_close_window.js] skip-if = !e10s # test relies on e10s behavior [browser_download_always_ask_preferred_app.js] +[browser_ext_helper_pb.js] [browser_remember_download_option.js] [browser_web_protocol_handlers.js] diff --git a/uriloader/exthandler/tests/mochitest/browser_ext_helper_pb.js b/uriloader/exthandler/tests/mochitest/browser_ext_helper_pb.js new file mode 100644 index 000000000000..d095198b02ce --- /dev/null +++ b/uriloader/exthandler/tests/mochitest/browser_ext_helper_pb.js @@ -0,0 +1,74 @@ +const { Downloads } = Cu.import("resource://gre/modules/Downloads.jsm", {}); +const URI = "http://example.com/browser/uriloader/exthandler/tests/mochitest/blob.html"; + +add_task(async function testExtHelperInPrivateBrowsing() { + let win = await BrowserTestUtils.openNewBrowserWindow({private: true}); + let browser = win.gBrowser.selectedBrowser; + browser.loadURI(URI); + await BrowserTestUtils.browserLoaded(browser); + + let listener = { + _resolve: null, + + onOpenWindow (aXULWindow) { + info("Download window shown..."); + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + waitForFocus(function() { + // When the download dialog is shown, its accept button is only enabled + // after 1000ms (which is configured in the pref "security.dialog_enable_delay", + // see SharedPromptUtils.jsm. + setTimeout(() => { + // Set the choice to 'Save As File'. + let mode = domwindow.document.getElementById("mode"); + let save = domwindow.document.getElementById("save"); + let index = mode.getIndexOfItem(save); + mode.selectedItem = save; + domwindow.document.documentElement.acceptDialog(); + // acceptDialog will close domwindow itself, so we don't have to close + // domwindow here. + }, 1000); + }, domwindow); + }, + + onCloseWindow: function(aXULWindow) { + info("onCloseWindow"); + Services.wm.removeListener(listener); + this._resolve(); + }, + onWindowTitleChange: function(aXULWindow, aNewTitle) {}, + + waitForDownload: function() { + return new Promise(resolve => { + this._resolve = resolve; + }); + } + }; + + Services.wm.addListener(listener); + + await ContentTask.spawn(browser, {}, async function() { + var download = content.document.getElementById("download"); + download.click(); + }); + + // Wait until download is finished. + // However there seems to be no easy way to get notified when the download is + // completed, so we use onCloseWindow listener here. + await listener.waitForDownload(); + + let allList = await Downloads.getList(Downloads.ALL); + let allDownloads = await allList.getAll(); + Assert.equal(allDownloads.length, 1, "Should have at least 1 download in ALL mode"); + + let publicList = await Downloads.getList(Downloads.PUBLIC); + let publicDownloads = await publicList.getAll(); + Assert.equal(publicDownloads.length, 0, "Shouldn't have any download in normal mode"); + + let privateList = await Downloads.getList(Downloads.PRIVATE); + let privateDownloads = await privateList.getAll(); + Assert.equal(privateDownloads.length, 1, "Should have 1 download in private mode"); + + win.close(); + finish(); +}); diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h index 330ba3ecec23..459d5afceff1 100644 --- a/xpcom/base/nscore.h +++ b/xpcom/base/nscore.h @@ -194,6 +194,35 @@ typedef MozRefCountType nsrefcnt; +namespace mozilla { +// Extensions to the mozilla::Result type for handling of nsresult values. +// +// Note that these specializations need to be defined before Result.h is +// included, or we run into explicit specialization after instantiation errors, +// especially if Result.h is used in multiple sources in a unified compile. + +namespace detail { +// When used as an error value, nsresult should never be NS_OK. +// This specialization allows us to pack Result into a +// nsresult-sized value. +template struct UnusedZero; +template<> +struct UnusedZero +{ + static const bool value = true; +}; +} // namespace detail + +template class MOZ_MUST_USE_TYPE GenericErrorResult; +template <> class MOZ_MUST_USE_TYPE GenericErrorResult; + +struct Ok; +template class Result; + +// Allow MOZ_TRY to handle `nsresult` values. +inline Result ToResult(nsresult aValue); +} // namespace mozilla + /* * Use these macros to do 64bit safe pointer conversions. */ diff --git a/xpcom/tests/unit/xpcshell.ini b/xpcom/tests/unit/xpcshell.ini index 7c6ec60265e8..bc984c6048c6 100644 --- a/xpcom/tests/unit/xpcshell.ini +++ b/xpcom/tests/unit/xpcshell.ini @@ -39,7 +39,7 @@ skip-if = os == "win" || os == "linux" # bug 582821, bug 1325609 # Bug 676998: test fails consistently on Android fail-if = os == "android" [test_nsIProcess_stress.js] -skip-if = os == "win" # bug 676412, test isn't needed on windows and runs really slowly +skip-if = os == "win" || coverage # bug 676412, test isn't needed on windows and runs really slowly, bug 1394989 [test_pipe.js] [test_process_directives.js] skip-if = os == "android"