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,
"); + + 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 @@ + + +
+ + +
+