diff options
Diffstat (limited to 'toolkit/components/pdfjs/content/PdfjsParent.sys.mjs')
-rw-r--r-- | toolkit/components/pdfjs/content/PdfjsParent.sys.mjs | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/toolkit/components/pdfjs/content/PdfjsParent.sys.mjs b/toolkit/components/pdfjs/content/PdfjsParent.sys.mjs new file mode 100644 index 0000000000..6b7c38a23a --- /dev/null +++ b/toolkit/components/pdfjs/content/PdfjsParent.sys.mjs @@ -0,0 +1,339 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", + SetClipboardSearchString: "resource://gre/modules/Finder.sys.mjs", +}); + +var Svc = {}; +XPCOMUtils.defineLazyServiceGetter( + Svc, + "mime", + "@mozilla.org/mime;1", + "nsIMIMEService" +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "matchesCountLimit", + "accessibility.typeaheadfind.matchesCountLimit" +); + +let gFindTypes = [ + "find", + "findagain", + "findhighlightallchange", + "findcasesensitivitychange", + "findbarclose", + "finddiacriticmatchingchange", +]; + +export class PdfjsParent extends JSWindowActorParent { + constructor() { + super(); + this._boundToFindbar = null; + this._findFailedString = null; + } + + didDestroy() { + this._removeEventListener(); + } + + receiveMessage(aMsg) { + switch (aMsg.name) { + case "PDFJS:Parent:displayWarning": + this._displayWarning(aMsg); + break; + + case "PDFJS:Parent:updateControlState": + return this._updateControlState(aMsg); + case "PDFJS:Parent:updateMatchesCount": + return this._updateMatchesCount(aMsg); + case "PDFJS:Parent:addEventListener": + return this._addEventListener(); + case "PDFJS:Parent:saveURL": + return this._saveURL(aMsg); + } + return undefined; + } + + /* + * Internal + */ + + get browser() { + return this.browsingContext.top.embedderElement; + } + + _saveURL(aMsg) { + const data = aMsg.data; + this.browser.ownerGlobal.saveURL( + data.blobUrl /* aURL */, + data.originalUrl /* aOriginalURL */, + data.filename /* aFileName */, + null /* aFilePickerTitleKey */, + true /* aShouldBypassCache */, + false /* aSkipPrompt */, + null /* aReferrerInfo */, + null /* aCookieJarSettings*/, + null /* aSourceDocument */, + lazy.PrivateBrowsingUtils.isBrowserPrivate( + this.browser + ) /* aIsContentWindowPrivate */, + Services.scriptSecurityManager.getSystemPrincipal() /* aPrincipal */ + ); + } + + _updateControlState(aMsg) { + let data = aMsg.data; + let browser = this.browser; + let tabbrowser = browser.getTabBrowser(); + let tab = tabbrowser.getTabForBrowser(browser); + tabbrowser.getFindBar(tab).then(fb => { + if (!fb) { + // The tab or window closed. + return; + } + fb.updateControlState(data.result, data.findPrevious); + + if ( + data.result === Ci.nsITypeAheadFind.FIND_FOUND || + data.result === Ci.nsITypeAheadFind.FIND_WRAPPED || + (data.result === Ci.nsITypeAheadFind.FIND_PENDING && + !this._findFailedString) + ) { + this._findFailedString = null; + lazy.SetClipboardSearchString(data.rawQuery); + } else if (!this._findFailedString) { + this._findFailedString = data.rawQuery; + lazy.SetClipboardSearchString(data.rawQuery); + } + + const matchesCount = this._requestMatchesCount(data.matchesCount); + fb.onMatchesCountResult(matchesCount); + }); + } + + _updateMatchesCount(aMsg) { + let data = aMsg.data; + let browser = this.browser; + let tabbrowser = browser.getTabBrowser(); + let tab = tabbrowser.getTabForBrowser(browser); + tabbrowser.getFindBar(tab).then(fb => { + if (!fb) { + // The tab or window closed. + return; + } + const matchesCount = this._requestMatchesCount(data); + fb.onMatchesCountResult(matchesCount); + }); + } + + _requestMatchesCount(data) { + if (!data) { + return { current: 0, total: 0 }; + } + let result = { + current: data.current, + total: data.total, + limit: + typeof lazy.matchesCountLimit === "number" ? lazy.matchesCountLimit : 0, + }; + if (result.total > result.limit) { + result.total = -1; + } + return result; + } + + handleEvent(aEvent) { + const type = aEvent.type; + // Handle the tab find initialized event specially: + if (type == "TabFindInitialized") { + let browser = aEvent.target.linkedBrowser; + this._hookupEventListeners(browser); + aEvent.target.removeEventListener(type, this); + return; + } + + if (type == "SwapDocShells") { + this._removeEventListener(); + let newBrowser = aEvent.detail; + newBrowser.addEventListener( + "EndSwapDocShells", + evt => { + this._hookupEventListeners(newBrowser); + }, + { once: true } + ); + return; + } + + // Ignore events findbar events which arrive while the Pdfjs document is in + // the BFCache. + if (this.windowContext.isInBFCache) { + return; + } + + // To avoid forwarding the message as a CPOW, create a structured cloneable + // version of the event for both performance, and ease of usage, reasons. + let detail = null; + if (type !== "findbarclose") { + detail = { + query: aEvent.detail.query, + caseSensitive: aEvent.detail.caseSensitive, + entireWord: aEvent.detail.entireWord, + highlightAll: aEvent.detail.highlightAll, + findPrevious: aEvent.detail.findPrevious, + matchDiacritics: aEvent.detail.matchDiacritics, + }; + } + + let browser = aEvent.currentTarget.browser; + if (!this._boundToFindbar) { + throw new Error( + "FindEventManager was not bound for the current browser." + ); + } + browser.sendMessageToActor( + "PDFJS:Child:handleEvent", + { type, detail }, + "Pdfjs" + ); + aEvent.preventDefault(); + } + + _addEventListener() { + let browser = this.browser; + if (this._boundToFindbar) { + throw new Error( + "FindEventManager was bound 2nd time without unbinding it first." + ); + } + + this._hookupEventListeners(browser); + } + + /** + * Either hook up all the find event listeners if a findbar exists, + * or listen for a find bar being created and hook up event listeners + * when it does get created. + */ + _hookupEventListeners(aBrowser) { + let tabbrowser = aBrowser.getTabBrowser(); + let tab = tabbrowser.getTabForBrowser(aBrowser); + let findbar = tabbrowser.getCachedFindBar(tab); + if (findbar) { + // And we need to start listening to find events. + for (var i = 0; i < gFindTypes.length; i++) { + var type = gFindTypes[i]; + findbar.addEventListener(type, this, true); + } + this._boundToFindbar = findbar; + } else { + tab.addEventListener("TabFindInitialized", this); + } + aBrowser.addEventListener("SwapDocShells", this); + return !!findbar; + } + + _removeEventListener() { + let browser = this.browser; + + // make sure the listener has been removed. + let findbar = this._boundToFindbar; + if (findbar) { + // No reason to listen to find events any longer. + for (var i = 0; i < gFindTypes.length; i++) { + var type = gFindTypes[i]; + findbar.removeEventListener(type, this, true); + } + } else if (browser) { + // If we registered a `TabFindInitialized` listener which never fired, + // make sure we remove it. + let tabbrowser = browser.getTabBrowser(); + let tab = tabbrowser.getTabForBrowser(browser); + tab?.removeEventListener("TabFindInitialized", this); + } + + this._boundToFindbar = null; + + // Clean up any SwapDocShells event listeners. + browser?.removeEventListener("SwapDocShells", this); + } + + /* + * Display a notification warning when the renderer isn't sure + * a pdf displayed correctly. + */ + _displayWarning(aMsg) { + let data = aMsg.data; + let browser = this.browser; + + let tabbrowser = browser.getTabBrowser(); + let notificationBox = tabbrowser.getNotificationBox(browser); + + // Flag so we don't send the message twice, since if the user clicks + // "open with different viewer" both the button callback and + // eventCallback will be called. + let messageSent = false; + let sendMessage = download => { + // Don't send a response again if we already responded when the button was + // clicked. + if (messageSent) { + return; + } + try { + this.sendAsyncMessage("PDFJS:Child:fallbackDownload", { download }); + messageSent = true; + } catch (ex) { + // Ignore any exception if it is related to the child + // getting destroyed before the message can be sent. + if (!/JSWindowActorParent cannot send at the moment/.test(ex.message)) { + throw ex; + } + } + }; + let buttons = [ + { + label: data.label, + accessKey: data.accessKey, + callback() { + sendMessage(true); + }, + }, + ]; + notificationBox.appendNotification( + "pdfjs-fallback", + { + label: data.message, + priority: notificationBox.PRIORITY_INFO_MEDIUM, + eventCallback: eventType => { + // Currently there is only one event "removed" but if there are any other + // added in the future we still only care about removed at the moment. + if (eventType !== "removed") { + return; + } + sendMessage(false); + }, + }, + buttons + ); + } +} |