| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | /* 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 {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); | 
					
						
							|  |  |  | const Ci = Components.interfaces; | 
					
						
							|  |  |  | const Cr = Components.results; | 
					
						
							|  |  |  | const Cu = Components.utils; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | 
					
						
							|  |  |  | const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm'); | 
					
						
							|  |  |  | const dragService = Cc["@mozilla.org/widget/dragservice;1"].getService( | 
					
						
							|  |  |  |   Ci.nsIDragService | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | const obs = Cc["@mozilla.org/observer-service;1"].getService( | 
					
						
							|  |  |  |   Ci.nsIObserverService | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const helper = new Helper(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WorkerData { | 
					
						
							| 
									
										
										
										
											2020-10-02 04:13:42 -07:00
										 |  |  |   constructor(pageAgent, browserChannel, worker) { | 
					
						
							|  |  |  |     this._workerRuntime = worker.channel().connect('runtime'); | 
					
						
							|  |  |  |     this._browserWorker = browserChannel.connect(worker.id()); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._worker = worker; | 
					
						
							|  |  |  |     const emit = name => { | 
					
						
							|  |  |  |       return (...args) => this._browserWorker.emit(name, ...args); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     this._eventListeners = [ | 
					
						
							| 
									
										
										
										
											2020-10-02 04:13:42 -07:00
										 |  |  |       worker.channel().register('runtime', { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |         runtimeConsole: emit('runtimeConsole'), | 
					
						
							|  |  |  |         runtimeExecutionContextCreated: emit('runtimeExecutionContextCreated'), | 
					
						
							|  |  |  |         runtimeExecutionContextDestroyed: emit('runtimeExecutionContextDestroyed'), | 
					
						
							|  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2020-10-02 04:13:42 -07:00
										 |  |  |       browserChannel.register(worker.id(), { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |         evaluate: (options) => this._workerRuntime.send('evaluate', options), | 
					
						
							|  |  |  |         callFunction: (options) => this._workerRuntime.send('callFunction', options), | 
					
						
							|  |  |  |         getObjectProperties: (options) => this._workerRuntime.send('getObjectProperties', options), | 
					
						
							| 
									
										
										
										
											2021-04-01 11:29:33 -05:00
										 |  |  |         disposeObject: (options) => this._workerRuntime.send('disposeObject', options), | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       }), | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   dispose() { | 
					
						
							|  |  |  |     this._workerRuntime.dispose(); | 
					
						
							|  |  |  |     this._browserWorker.dispose(); | 
					
						
							|  |  |  |     helper.removeListeners(this._eventListeners); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PageAgent { | 
					
						
							| 
									
										
										
										
											2020-10-02 04:13:42 -07:00
										 |  |  |   constructor(messageManager, browserChannel, frameTree) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._messageManager = messageManager; | 
					
						
							|  |  |  |     this._browserChannel = browserChannel; | 
					
						
							| 
									
										
										
										
											2020-10-02 04:13:42 -07:00
										 |  |  |     this._browserPage = browserChannel.connect('page'); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._frameTree = frameTree; | 
					
						
							|  |  |  |     this._runtime = frameTree.runtime(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._workerData = new Map(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  |     const docShell = frameTree.mainFrame().docShell(); | 
					
						
							|  |  |  |     this._docShell = docShell; | 
					
						
							|  |  |  |     this._initialDPPX = docShell.contentViewer.overrideDPPX; | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |     this._dragging = false; | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Dispatch frameAttached events for all initial frames
 | 
					
						
							|  |  |  |     for (const frame of this._frameTree.frames()) { | 
					
						
							|  |  |  |       this._onFrameAttached(frame); | 
					
						
							|  |  |  |       if (frame.url()) | 
					
						
							|  |  |  |         this._onNavigationCommitted(frame); | 
					
						
							|  |  |  |       if (frame.pendingNavigationId()) | 
					
						
							|  |  |  |         this._onNavigationStarted(frame); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Report created workers.
 | 
					
						
							|  |  |  |     for (const worker of this._frameTree.workers()) | 
					
						
							|  |  |  |       this._onWorkerCreated(worker); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Report execution contexts.
 | 
					
						
							|  |  |  |     for (const context of this._runtime.executionContexts()) | 
					
						
							|  |  |  |       this._onExecutionContextCreated(context); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this._frameTree.isPageReady()) { | 
					
						
							|  |  |  |       this._browserPage.emit('pageReady', {}); | 
					
						
							|  |  |  |       const mainFrame = this._frameTree.mainFrame(); | 
					
						
							|  |  |  |       const domWindow = mainFrame.domWindow(); | 
					
						
							|  |  |  |       const document = domWindow ? domWindow.document : null; | 
					
						
							|  |  |  |       const readyState = document ? document.readyState : null; | 
					
						
							|  |  |  |       // Sometimes we initialize later than the first about:blank page is opened.
 | 
					
						
							|  |  |  |       // In this case, the page might've been loaded already, and we need to issue
 | 
					
						
							|  |  |  |       // the `DOMContentLoaded` and `load` events.
 | 
					
						
							|  |  |  |       if (mainFrame.url() === 'about:blank' && readyState === 'complete') | 
					
						
							|  |  |  |         this._emitAllEvents(this._frameTree.mainFrame()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._eventListeners = [ | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  |       helper.addObserver(this._linkClicked.bind(this, false), 'juggler-link-click'), | 
					
						
							|  |  |  |       helper.addObserver(this._linkClicked.bind(this, true), 'juggler-link-click-sync'), | 
					
						
							|  |  |  |       helper.addObserver(this._onWindowOpenInNewContext.bind(this), 'juggler-window-open-in-new-context'), | 
					
						
							|  |  |  |       helper.addObserver(this._filePickerShown.bind(this), 'juggler-file-picker-shown'), | 
					
						
							|  |  |  |       helper.addEventListener(this._messageManager, 'DOMContentLoaded', this._onDOMContentLoaded.bind(this)), | 
					
						
							|  |  |  |       helper.addObserver(this._onDocumentOpenLoad.bind(this), 'juggler-document-open-loaded'), | 
					
						
							|  |  |  |       helper.addEventListener(this._messageManager, 'error', this._onError.bind(this)), | 
					
						
							| 
									
										
										
										
											2020-10-07 09:22:56 -07:00
										 |  |  |       helper.on(this._frameTree, 'load', this._onLoad.bind(this)), | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  |       helper.on(this._frameTree, 'frameattached', this._onFrameAttached.bind(this)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'framedetached', this._onFrameDetached.bind(this)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'navigationstarted', this._onNavigationStarted.bind(this)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'navigationcommitted', this._onNavigationCommitted.bind(this)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'samedocumentnavigation', this._onSameDocumentNavigation.bind(this)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'pageready', () => this._browserPage.emit('pageReady', {})), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'workercreated', this._onWorkerCreated.bind(this)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'workerdestroyed', this._onWorkerDestroyed.bind(this)), | 
					
						
							| 
									
										
										
										
											2020-10-29 16:33:50 -07:00
										 |  |  |       helper.on(this._frameTree, 'websocketcreated', event => this._browserPage.emit('webSocketCreated', event)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'websocketopened', event => this._browserPage.emit('webSocketOpened', event)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'websocketframesent', event => this._browserPage.emit('webSocketFrameSent', event)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'websocketframereceived', event => this._browserPage.emit('webSocketFrameReceived', event)), | 
					
						
							|  |  |  |       helper.on(this._frameTree, 'websocketclosed', event => this._browserPage.emit('webSocketClosed', event)), | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  |       helper.addObserver(this._onWindowOpen.bind(this), 'webNavigation-createdNavigationTarget-from-js'), | 
					
						
							|  |  |  |       this._runtime.events.onErrorFromWorker((domWindow, message, stack) => { | 
					
						
							|  |  |  |         const frame = this._frameTree.frameForDocShell(domWindow.docShell); | 
					
						
							|  |  |  |         if (!frame) | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         this._browserPage.emit('pageUncaughtError', { | 
					
						
							|  |  |  |           frameId: frame.id(), | 
					
						
							|  |  |  |           message, | 
					
						
							|  |  |  |           stack, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }), | 
					
						
							|  |  |  |       this._runtime.events.onConsoleMessage(msg => this._browserPage.emit('runtimeConsole', msg)), | 
					
						
							|  |  |  |       this._runtime.events.onExecutionContextCreated(this._onExecutionContextCreated.bind(this)), | 
					
						
							|  |  |  |       this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)), | 
					
						
							| 
									
										
										
										
											2021-05-11 16:27:39 +00:00
										 |  |  |       this._runtime.events.onBindingCalled(this._onBindingCalled.bind(this)), | 
					
						
							| 
									
										
										
										
											2020-10-02 04:13:42 -07:00
										 |  |  |       browserChannel.register('page', { | 
					
						
							| 
									
										
										
										
											2021-05-11 16:27:39 +00:00
										 |  |  |         addBinding: ({ worldName, name, script }) => this._frameTree.addBinding(worldName, name, script), | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |         addScriptToEvaluateOnNewDocument: ({script, worldName}) => this._frameTree.addScriptToEvaluateOnNewDocument(script, worldName), | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |         adoptNode: this._adoptNode.bind(this), | 
					
						
							|  |  |  |         crash: this._crash.bind(this), | 
					
						
							|  |  |  |         describeNode: this._describeNode.bind(this), | 
					
						
							|  |  |  |         dispatchKeyEvent: this._dispatchKeyEvent.bind(this), | 
					
						
							|  |  |  |         dispatchMouseEvent: this._dispatchMouseEvent.bind(this), | 
					
						
							| 
									
										
										
										
											2021-08-25 11:27:12 -04:00
										 |  |  |         dispatchWheelEvent: this._dispatchWheelEvent.bind(this), | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |         dispatchTouchEvent: this._dispatchTouchEvent.bind(this), | 
					
						
							| 
									
										
										
										
											2020-10-13 12:24:11 -07:00
										 |  |  |         dispatchTapEvent: this._dispatchTapEvent.bind(this), | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |         getContentQuads: this._getContentQuads.bind(this), | 
					
						
							|  |  |  |         getFullAXTree: this._getFullAXTree.bind(this), | 
					
						
							|  |  |  |         goBack: this._goBack.bind(this), | 
					
						
							|  |  |  |         goForward: this._goForward.bind(this), | 
					
						
							|  |  |  |         insertText: this._insertText.bind(this), | 
					
						
							|  |  |  |         navigate: this._navigate.bind(this), | 
					
						
							|  |  |  |         reload: this._reload.bind(this), | 
					
						
							|  |  |  |         screenshot: this._screenshot.bind(this), | 
					
						
							|  |  |  |         scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this), | 
					
						
							|  |  |  |         setCacheDisabled: this._setCacheDisabled.bind(this), | 
					
						
							|  |  |  |         setFileInputFiles: this._setFileInputFiles.bind(this), | 
					
						
							|  |  |  |         setInterceptFileChooserDialog: this._setInterceptFileChooserDialog.bind(this), | 
					
						
							|  |  |  |         evaluate: this._runtime.evaluate.bind(this._runtime), | 
					
						
							|  |  |  |         callFunction: this._runtime.callFunction.bind(this._runtime), | 
					
						
							|  |  |  |         getObjectProperties: this._runtime.getObjectProperties.bind(this._runtime), | 
					
						
							|  |  |  |         disposeObject: this._runtime.disposeObject.bind(this._runtime), | 
					
						
							|  |  |  |       }), | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _setCacheDisabled({cacheDisabled}) { | 
					
						
							|  |  |  |     const enable = Ci.nsIRequest.LOAD_NORMAL; | 
					
						
							|  |  |  |     const disable = Ci.nsIRequest.LOAD_BYPASS_CACHE | | 
					
						
							|  |  |  |                   Ci.nsIRequest.INHIBIT_CACHING; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const docShell = this._frameTree.mainFrame().docShell(); | 
					
						
							|  |  |  |     docShell.defaultLoadFlags = cacheDisabled ? disable : enable; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 16:05:34 -07:00
										 |  |  |   _emitAllEvents(frame) { | 
					
						
							|  |  |  |     this._browserPage.emit('pageEventFired', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       name: 'DOMContentLoaded', | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     this._browserPage.emit('pageEventFired', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       name: 'load', | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   _onExecutionContextCreated(executionContext) { | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  |     this._browserPage.emit('runtimeExecutionContextCreated', { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       executionContextId: executionContext.id(), | 
					
						
							|  |  |  |       auxData: executionContext.auxData(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onExecutionContextDestroyed(executionContext) { | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  |     this._browserPage.emit('runtimeExecutionContextDestroyed', { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       executionContextId: executionContext.id(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onWorkerCreated(worker) { | 
					
						
							| 
									
										
										
										
											2020-10-02 04:13:42 -07:00
										 |  |  |     const workerData = new WorkerData(this, this._browserChannel, worker); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._workerData.set(worker.id(), workerData); | 
					
						
							|  |  |  |     this._browserPage.emit('pageWorkerCreated', { | 
					
						
							|  |  |  |       workerId: worker.id(), | 
					
						
							|  |  |  |       frameId: worker.frame().id(), | 
					
						
							|  |  |  |       url: worker.url(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onWorkerDestroyed(worker) { | 
					
						
							|  |  |  |     const workerData = this._workerData.get(worker.id()); | 
					
						
							|  |  |  |     if (!workerData) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     this._workerData.delete(worker.id()); | 
					
						
							|  |  |  |     workerData.dispose(); | 
					
						
							|  |  |  |     this._browserPage.emit('pageWorkerDestroyed', { | 
					
						
							|  |  |  |       workerId: worker.id(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onWindowOpen(subject) { | 
					
						
							|  |  |  |     if (!(subject instanceof Ci.nsIPropertyBag2)) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     const props = subject.QueryInterface(Ci.nsIPropertyBag2); | 
					
						
							|  |  |  |     const hasUrl = props.hasKey('url'); | 
					
						
							|  |  |  |     const createdDocShell = props.getPropertyAsInterface('createdTabDocShell', Ci.nsIDocShell); | 
					
						
							| 
									
										
										
										
											2020-08-11 16:05:34 -07:00
										 |  |  |     if (!hasUrl && createdDocShell === this._docShell && this._frameTree.forcePageReady()) | 
					
						
							|  |  |  |       this._emitAllEvents(this._frameTree.mainFrame()); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _setInterceptFileChooserDialog({enabled}) { | 
					
						
							|  |  |  |     this._docShell.fileInputInterceptionEnabled = !!enabled; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _linkClicked(sync, anchorElement) { | 
					
						
							|  |  |  |     if (anchorElement.ownerGlobal.docShell !== this._docShell) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     this._browserPage.emit('pageLinkClicked', { phase: sync ? 'after' : 'before' }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onWindowOpenInNewContext(docShell) { | 
					
						
							|  |  |  |     // TODO: unify this with _onWindowOpen if possible.
 | 
					
						
							|  |  |  |     const frame = this._frameTree.frameForDocShell(docShell); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     this._browserPage.emit('pageWillOpenNewWindowAsynchronously'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _filePickerShown(inputElement) { | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     const frame = this._findFrameForNode(inputElement); | 
					
						
							| 
									
										
										
										
											2021-08-27 19:28:24 +03:00
										 |  |  |     if (!frame) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._browserPage.emit('pageFileChooserOpened', { | 
					
						
							| 
									
										
										
										
											2021-05-12 15:52:08 +00:00
										 |  |  |       executionContextId: frame.mainExecutionContext().id(), | 
					
						
							|  |  |  |       element: frame.mainExecutionContext().rawValueToRemoteObject(inputElement) | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _findFrameForNode(node) { | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     return this._frameTree.frames().find(frame => { | 
					
						
							|  |  |  |       const doc = frame.domWindow().document; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return node === doc || node.ownerDocument === doc; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onDOMContentLoaded(event) { | 
					
						
							| 
									
										
										
										
											2020-07-01 13:28:13 -07:00
										 |  |  |     if (!event.target.ownerGlobal) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     const docShell = event.target.ownerGlobal.docShell; | 
					
						
							|  |  |  |     const frame = this._frameTree.frameForDocShell(docShell); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     this._browserPage.emit('pageEventFired', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       name: 'DOMContentLoaded', | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onError(errorEvent) { | 
					
						
							|  |  |  |     const docShell = errorEvent.target.ownerGlobal.docShell; | 
					
						
							|  |  |  |     const frame = this._frameTree.frameForDocShell(docShell); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     this._browserPage.emit('pageUncaughtError', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       message: errorEvent.message, | 
					
						
							| 
									
										
										
										
											2020-10-19 02:23:53 -07:00
										 |  |  |       stack: errorEvent.error && typeof errorEvent.error.stack === 'string' ? errorEvent.error.stack : '', | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onDocumentOpenLoad(document) { | 
					
						
							|  |  |  |     const docShell = document.ownerGlobal.docShell; | 
					
						
							|  |  |  |     const frame = this._frameTree.frameForDocShell(docShell); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     this._browserPage.emit('pageEventFired', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       name: 'load' | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-07 09:22:56 -07:00
										 |  |  |   _onLoad(frame) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._browserPage.emit('pageEventFired', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       name: 'load' | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onNavigationStarted(frame) { | 
					
						
							|  |  |  |     this._browserPage.emit('pageNavigationStarted', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       navigationId: frame.pendingNavigationId(), | 
					
						
							|  |  |  |       url: frame.pendingNavigationURL(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onNavigationAborted(frame, navigationId, errorText) { | 
					
						
							|  |  |  |     this._browserPage.emit('pageNavigationAborted', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       navigationId, | 
					
						
							|  |  |  |       errorText, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     if (!frame._initialNavigationDone && frame !== this._frameTree.mainFrame()) | 
					
						
							| 
									
										
										
										
											2020-08-11 16:05:34 -07:00
										 |  |  |       this._emitAllEvents(frame); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     frame._initialNavigationDone = true; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onSameDocumentNavigation(frame) { | 
					
						
							|  |  |  |     this._browserPage.emit('pageSameDocumentNavigation', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       url: frame.url(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onNavigationCommitted(frame) { | 
					
						
							|  |  |  |     this._browserPage.emit('pageNavigationCommitted', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       navigationId: frame.lastCommittedNavigationId() || undefined, | 
					
						
							|  |  |  |       url: frame.url(), | 
					
						
							|  |  |  |       name: frame.name(), | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     frame._initialNavigationDone = true; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onFrameAttached(frame) { | 
					
						
							|  |  |  |     this._browserPage.emit('pageFrameAttached', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |       parentFrameId: frame.parentFrame() ? frame.parentFrame().id() : undefined, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onFrameDetached(frame) { | 
					
						
							|  |  |  |     this._browserPage.emit('pageFrameDetached', { | 
					
						
							|  |  |  |       frameId: frame.id(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 16:27:39 +00:00
										 |  |  |   _onBindingCalled({executionContextId, name, payload}) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._browserPage.emit('pageBindingCalled', { | 
					
						
							| 
									
										
										
										
											2021-05-11 16:27:39 +00:00
										 |  |  |       executionContextId, | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       name, | 
					
						
							|  |  |  |       payload | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   dispose() { | 
					
						
							|  |  |  |     for (const workerData of this._workerData.values()) | 
					
						
							|  |  |  |       workerData.dispose(); | 
					
						
							|  |  |  |     this._workerData.clear(); | 
					
						
							|  |  |  |     helper.removeListeners(this._eventListeners); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _navigate({frameId, url, referer}) { | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       const uri = NetUtil.newURI(url); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       throw new Error(`Invalid url: "${url}"`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     let referrerURI = null; | 
					
						
							|  |  |  |     let referrerInfo = null; | 
					
						
							|  |  |  |     if (referer) { | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         referrerURI = NetUtil.newURI(referer); | 
					
						
							|  |  |  |         const ReferrerInfo = Components.Constructor( | 
					
						
							|  |  |  |           '@mozilla.org/referrer-info;1', | 
					
						
							|  |  |  |           'nsIReferrerInfo', | 
					
						
							|  |  |  |           'init' | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, referrerURI); | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         throw new Error(`Invalid referer: "${referer}"`); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     const docShell = frame.docShell().QueryInterface(Ci.nsIWebNavigation); | 
					
						
							|  |  |  |     docShell.loadURI(url, { | 
					
						
							|  |  |  |       triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), | 
					
						
							|  |  |  |       flags: Ci.nsIWebNavigation.LOAD_FLAGS_NONE, | 
					
						
							|  |  |  |       referrerInfo, | 
					
						
							|  |  |  |       postData: null, | 
					
						
							|  |  |  |       headers: null, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     return {navigationId: frame.pendingNavigationId(), navigationURL: frame.pendingNavigationURL()}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _reload({frameId, url}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     const docShell = frame.docShell().QueryInterface(Ci.nsIWebNavigation); | 
					
						
							|  |  |  |     docShell.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _goBack({frameId, url}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     const docShell = frame.docShell(); | 
					
						
							|  |  |  |     if (!docShell.canGoBack) | 
					
						
							| 
									
										
										
										
											2020-07-01 13:28:13 -07:00
										 |  |  |       return {success: false}; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     docShell.goBack(); | 
					
						
							| 
									
										
										
										
											2020-07-01 13:28:13 -07:00
										 |  |  |     return {success: true}; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _goForward({frameId, url}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     const docShell = frame.docShell(); | 
					
						
							|  |  |  |     if (!docShell.canGoForward) | 
					
						
							| 
									
										
										
										
											2020-07-01 13:28:13 -07:00
										 |  |  |       return {success: false}; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     docShell.goForward(); | 
					
						
							| 
									
										
										
										
											2020-07-01 13:28:13 -07:00
										 |  |  |     return {success: true}; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _adoptNode({frameId, objectId, executionContextId}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       throw new Error('Failed to find frame with id = ' + frameId); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     const unsafeObject = frame.unsafeObject(objectId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     const context = this._runtime.findExecutionContext(executionContextId); | 
					
						
							|  |  |  |     const fromPrincipal = unsafeObject.nodePrincipal; | 
					
						
							|  |  |  |     const toFrame = this._frameTree.frame(context.auxData().frameId); | 
					
						
							|  |  |  |     const toPrincipal = toFrame.domWindow().document.nodePrincipal; | 
					
						
							|  |  |  |     if (!toPrincipal.subsumes(fromPrincipal)) | 
					
						
							|  |  |  |       return { remoteObject: null }; | 
					
						
							|  |  |  |     return { remoteObject: context.rawValueToRemoteObject(unsafeObject) }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _setFileInputFiles({objectId, frameId, files}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       throw new Error('Failed to find frame with id = ' + frameId); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     const unsafeObject = frame.unsafeObject(objectId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (!unsafeObject) | 
					
						
							|  |  |  |       throw new Error('Object is not input!'); | 
					
						
							|  |  |  |     const nsFiles = await Promise.all(files.map(filePath => File.createFromFileName(filePath))); | 
					
						
							|  |  |  |     unsafeObject.mozSetFileArray(nsFiles); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _getContentQuads({objectId, frameId}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       throw new Error('Failed to find frame with id = ' + frameId); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     const unsafeObject = frame.unsafeObject(objectId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (!unsafeObject.getBoxQuads) | 
					
						
							|  |  |  |       throw new Error('RemoteObject is not a node'); | 
					
						
							|  |  |  |     const quads = unsafeObject.getBoxQuads({relativeTo: this._frameTree.mainFrame().domWindow().document}).map(quad => { | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         p1: {x: quad.p1.x, y: quad.p1.y}, | 
					
						
							|  |  |  |         p2: {x: quad.p2.x, y: quad.p2.y}, | 
					
						
							|  |  |  |         p3: {x: quad.p3.x, y: quad.p3.y}, | 
					
						
							|  |  |  |         p4: {x: quad.p4.x, y: quad.p4.y}, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     return {quads}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _describeNode({objectId, frameId}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       throw new Error('Failed to find frame with id = ' + frameId); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     const unsafeObject = frame.unsafeObject(objectId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     const browsingContextGroup = frame.docShell().browsingContext.group; | 
					
						
							|  |  |  |     const frames = this._frameTree.allFramesInBrowsingContextGroup(browsingContextGroup); | 
					
						
							|  |  |  |     let contentFrame; | 
					
						
							|  |  |  |     let ownerFrame; | 
					
						
							|  |  |  |     for (const frame of frames) { | 
					
						
							|  |  |  |       if (unsafeObject.contentWindow && frame.docShell() === unsafeObject.contentWindow.docShell) | 
					
						
							|  |  |  |         contentFrame = frame; | 
					
						
							|  |  |  |       const document = frame.domWindow().document; | 
					
						
							|  |  |  |       if (unsafeObject === document || unsafeObject.ownerDocument === document) | 
					
						
							|  |  |  |         ownerFrame = frame; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       contentFrameId: contentFrame ? contentFrame.id() : undefined, | 
					
						
							|  |  |  |       ownerFrameId: ownerFrame ? ownerFrame.id() : undefined, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _scrollIntoViewIfNeeded({objectId, frameId, rect}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.frame(frameId); | 
					
						
							|  |  |  |     if (!frame) | 
					
						
							|  |  |  |       throw new Error('Failed to find frame with id = ' + frameId); | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |     const unsafeObject = frame.unsafeObject(objectId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (!unsafeObject.isConnected) | 
					
						
							|  |  |  |       throw new Error('Node is detached from document'); | 
					
						
							|  |  |  |     if (!rect) | 
					
						
							|  |  |  |       rect = { x: -1, y: -1, width: -1, height: -1}; | 
					
						
							|  |  |  |     if (unsafeObject.scrollRectIntoViewIfNeeded) | 
					
						
							|  |  |  |       unsafeObject.scrollRectIntoViewIfNeeded(rect.x, rect.y, rect.width, rect.height); | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2020-06-16 21:38:32 -07:00
										 |  |  |       throw new Error('Node does not have a layout object'); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _getNodeBoundingBox(unsafeObject) { | 
					
						
							|  |  |  |     if (!unsafeObject.getBoxQuads) | 
					
						
							|  |  |  |       throw new Error('RemoteObject is not a node'); | 
					
						
							|  |  |  |     const quads = unsafeObject.getBoxQuads({relativeTo: this._frameTree.mainFrame().domWindow().document}); | 
					
						
							|  |  |  |     if (!quads.length) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     let x1 = Infinity; | 
					
						
							|  |  |  |     let y1 = Infinity; | 
					
						
							|  |  |  |     let x2 = -Infinity; | 
					
						
							|  |  |  |     let y2 = -Infinity; | 
					
						
							|  |  |  |     for (const quad of quads) { | 
					
						
							|  |  |  |       const boundingBox = quad.getBounds(); | 
					
						
							|  |  |  |       x1 = Math.min(boundingBox.x, x1); | 
					
						
							|  |  |  |       y1 = Math.min(boundingBox.y, y1); | 
					
						
							|  |  |  |       x2 = Math.max(boundingBox.x + boundingBox.width, x2); | 
					
						
							|  |  |  |       y2 = Math.max(boundingBox.y + boundingBox.height, y2); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return {x: x1, y: y1, width: x2 - x1, height: y2 - y1}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 14:51:02 -07:00
										 |  |  |   async _screenshot({mimeType, clip, omitDeviceScaleFactor}) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     const content = this._messageManager.content; | 
					
						
							|  |  |  |     if (clip) { | 
					
						
							| 
									
										
										
										
											2021-04-01 14:51:02 -07:00
										 |  |  |       const data = takeScreenshot(content, clip.x, clip.y, clip.width, clip.height, mimeType, omitDeviceScaleFactor); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return {data}; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-04-01 14:51:02 -07:00
										 |  |  |     const data = takeScreenshot(content, content.scrollX, content.scrollY, content.innerWidth, content.innerHeight, mimeType, omitDeviceScaleFactor); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     return {data}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _dispatchKeyEvent({type, keyCode, code, key, repeat, location, text}) { | 
					
						
							|  |  |  |     // key events don't fire if we are dragging.
 | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |     if (this._dragging) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       if (type === 'keydown' && key === 'Escape') | 
					
						
							|  |  |  |         this._cancelDragIfNeeded(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const frame = this._frameTree.mainFrame(); | 
					
						
							|  |  |  |     const tip = frame.textInputProcessor(); | 
					
						
							|  |  |  |     if (key === 'Meta' && Services.appinfo.OS !== 'Darwin') | 
					
						
							|  |  |  |       key = 'OS'; | 
					
						
							|  |  |  |     else if (key === 'OS' && Services.appinfo.OS === 'Darwin') | 
					
						
							|  |  |  |       key = 'Meta'; | 
					
						
							|  |  |  |     let keyEvent = new (frame.domWindow().KeyboardEvent)("", { | 
					
						
							|  |  |  |       key, | 
					
						
							|  |  |  |       code, | 
					
						
							|  |  |  |       location, | 
					
						
							|  |  |  |       repeat, | 
					
						
							|  |  |  |       keyCode | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     if (type === 'keydown') { | 
					
						
							|  |  |  |       if (text && text !== key) { | 
					
						
							|  |  |  |         tip.commitCompositionWith(text, keyEvent); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         const flags = 0; | 
					
						
							|  |  |  |         tip.keydown(keyEvent, flags); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else if (type === 'keyup') { | 
					
						
							|  |  |  |       if (text) | 
					
						
							|  |  |  |         throw new Error(`keyup does not support text option`); | 
					
						
							|  |  |  |       const flags = 0; | 
					
						
							|  |  |  |       tip.keyup(keyEvent, flags); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       throw new Error(`Unknown type ${type}`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _dispatchTouchEvent({type, touchPoints, modifiers}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.mainFrame(); | 
					
						
							|  |  |  |     const defaultPrevented = frame.domWindow().windowUtils.sendTouchEvent( | 
					
						
							|  |  |  |       type.toLowerCase(), | 
					
						
							|  |  |  |       touchPoints.map((point, id) => id), | 
					
						
							|  |  |  |       touchPoints.map(point => point.x), | 
					
						
							|  |  |  |       touchPoints.map(point => point.y), | 
					
						
							|  |  |  |       touchPoints.map(point => point.radiusX === undefined ? 1.0 : point.radiusX), | 
					
						
							|  |  |  |       touchPoints.map(point => point.radiusY === undefined ? 1.0 : point.radiusY), | 
					
						
							|  |  |  |       touchPoints.map(point => point.rotationAngle === undefined ? 0.0 : point.rotationAngle), | 
					
						
							|  |  |  |       touchPoints.map(point => point.force === undefined ? 1.0 : point.force), | 
					
						
							|  |  |  |       touchPoints.length, | 
					
						
							|  |  |  |       modifiers); | 
					
						
							|  |  |  |     return {defaultPrevented}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 12:24:11 -07:00
										 |  |  |   async _dispatchTapEvent({x, y, modifiers}) { | 
					
						
							| 
									
										
										
										
											2020-11-16 11:07:06 -08:00
										 |  |  |     // Force a layout at the point in question, because touch events
 | 
					
						
							|  |  |  |     // do not seem to trigger one like mouse events.
 | 
					
						
							|  |  |  |     this._frameTree.mainFrame().domWindow().windowUtils.elementFromPoint( | 
					
						
							|  |  |  |       x, | 
					
						
							|  |  |  |       y, | 
					
						
							|  |  |  |       false /* aIgnoreRootScrollFrame */, | 
					
						
							|  |  |  |       true /* aFlushLayout */); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 12:24:11 -07:00
										 |  |  |     const {defaultPrevented: startPrevented} = await this._dispatchTouchEvent({ | 
					
						
							|  |  |  |       type: 'touchstart', | 
					
						
							|  |  |  |       modifiers, | 
					
						
							|  |  |  |       touchPoints: [{x, y}] | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     const {defaultPrevented: endPrevented} = await this._dispatchTouchEvent({ | 
					
						
							|  |  |  |       type: 'touchend', | 
					
						
							|  |  |  |       modifiers, | 
					
						
							|  |  |  |       touchPoints: [{x, y}] | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     if (startPrevented || endPrevented) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const frame = this._frameTree.mainFrame(); | 
					
						
							|  |  |  |     frame.domWindow().windowUtils.sendMouseEvent( | 
					
						
							|  |  |  |       'mousemove', | 
					
						
							|  |  |  |       x, | 
					
						
							|  |  |  |       y, | 
					
						
							|  |  |  |       0 /*button*/, | 
					
						
							|  |  |  |       0 /*clickCount*/, | 
					
						
							|  |  |  |       modifiers, | 
					
						
							|  |  |  |       false /*aIgnoreRootScrollFrame*/, | 
					
						
							|  |  |  |       undefined /*pressure*/, | 
					
						
							|  |  |  |       5 /*inputSource*/, | 
					
						
							|  |  |  |       undefined /*isDOMEventSynthesized*/, | 
					
						
							|  |  |  |       false /*isWidgetEventSynthesized*/, | 
					
						
							|  |  |  |       0 /*buttons*/, | 
					
						
							|  |  |  |       undefined /*pointerIdentifier*/, | 
					
						
							|  |  |  |       true /*disablePointerEvent*/); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     frame.domWindow().windowUtils.sendMouseEvent( | 
					
						
							|  |  |  |       'mousedown', | 
					
						
							|  |  |  |       x, | 
					
						
							|  |  |  |       y, | 
					
						
							|  |  |  |       0 /*button*/, | 
					
						
							|  |  |  |       1 /*clickCount*/, | 
					
						
							|  |  |  |       modifiers, | 
					
						
							|  |  |  |       false /*aIgnoreRootScrollFrame*/, | 
					
						
							|  |  |  |       undefined /*pressure*/, | 
					
						
							|  |  |  |       5 /*inputSource*/, | 
					
						
							|  |  |  |       undefined /*isDOMEventSynthesized*/, | 
					
						
							|  |  |  |       false /*isWidgetEventSynthesized*/, | 
					
						
							|  |  |  |       1 /*buttons*/, | 
					
						
							|  |  |  |       undefined /*pointerIdentifier*/, | 
					
						
							|  |  |  |       true /*disablePointerEvent*/); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     frame.domWindow().windowUtils.sendMouseEvent( | 
					
						
							|  |  |  |       'mouseup', | 
					
						
							|  |  |  |       x, | 
					
						
							|  |  |  |       y, | 
					
						
							|  |  |  |       0 /*button*/, | 
					
						
							|  |  |  |       1 /*clickCount*/, | 
					
						
							|  |  |  |       modifiers, | 
					
						
							|  |  |  |       false /*aIgnoreRootScrollFrame*/, | 
					
						
							|  |  |  |       undefined /*pressure*/, | 
					
						
							|  |  |  |       5 /*inputSource*/, | 
					
						
							|  |  |  |       undefined /*isDOMEventSynthesized*/, | 
					
						
							|  |  |  |       false /*isWidgetEventSynthesized*/, | 
					
						
							|  |  |  |       0 /*buttons*/, | 
					
						
							|  |  |  |       undefined /*pointerIdentifier*/, | 
					
						
							|  |  |  |       true /*disablePointerEvent*/); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   _startDragSessionIfNeeded() { | 
					
						
							|  |  |  |     const sess = dragService.getCurrentSession(); | 
					
						
							|  |  |  |     if (sess) return; | 
					
						
							|  |  |  |     dragService.startDragSessionForTests( | 
					
						
							|  |  |  |       Ci.nsIDragService.DRAGDROP_ACTION_MOVE | | 
					
						
							|  |  |  |         Ci.nsIDragService.DRAGDROP_ACTION_COPY | | 
					
						
							|  |  |  |         Ci.nsIDragService.DRAGDROP_ACTION_LINK | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _simulateDragEvent(type, x, y, modifiers) { | 
					
						
							|  |  |  |     const window = this._frameTree.mainFrame().domWindow(); | 
					
						
							|  |  |  |     const element = window.windowUtils.elementFromPoint(x, y, false, false); | 
					
						
							|  |  |  |     const event = window.document.createEvent('DragEvent'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     event.initDragEvent( | 
					
						
							|  |  |  |       type, | 
					
						
							|  |  |  |       true /* bubble */, | 
					
						
							|  |  |  |       true /* cancelable */, | 
					
						
							|  |  |  |       window, | 
					
						
							|  |  |  |       0 /* clickCount */, | 
					
						
							|  |  |  |       window.mozInnerScreenX + x, | 
					
						
							|  |  |  |       window.mozInnerScreenY + y, | 
					
						
							|  |  |  |       x, | 
					
						
							|  |  |  |       y, | 
					
						
							|  |  |  |       modifiers & 2 /* ctrlkey */, | 
					
						
							|  |  |  |       modifiers & 1 /* altKey */, | 
					
						
							|  |  |  |       modifiers & 4 /* shiftKey */, | 
					
						
							|  |  |  |       modifiers & 8 /* metaKey */, | 
					
						
							|  |  |  |       0 /* button */, // firefox always has the button as 0 on drops, regardless of which was pressed
 | 
					
						
							|  |  |  |       null /* relatedTarget */, | 
					
						
							| 
									
										
										
										
											2021-03-04 17:20:45 -08:00
										 |  |  |       null, | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-02-23 15:21:29 -08:00
										 |  |  |     if (type !== 'drop' || dragService.dragAction) | 
					
						
							|  |  |  |       window.windowUtils.dispatchDOMEventViaPresShellForTesting(element, event); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (type === 'drop') | 
					
						
							| 
									
										
										
										
											2021-02-23 15:21:29 -08:00
										 |  |  |       this._cancelDragIfNeeded(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _cancelDragIfNeeded() { | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |     this._dragging = false; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     const sess = dragService.getCurrentSession(); | 
					
						
							|  |  |  |     if (sess) | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |       dragService.endDragSession(true); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _dispatchMouseEvent({type, x, y, button, clickCount, modifiers, buttons}) { | 
					
						
							|  |  |  |     this._startDragSessionIfNeeded(); | 
					
						
							|  |  |  |     const trapDrag = subject => { | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |       this._dragging = true; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |     // Don't send mouse events if there is an active drag
 | 
					
						
							|  |  |  |     if (!this._dragging) { | 
					
						
							|  |  |  |       const frame = this._frameTree.mainFrame(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |       obs.addObserver(trapDrag, 'on-datatransfer-available'); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       frame.domWindow().windowUtils.sendMouseEvent( | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |         type, | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |         x, | 
					
						
							|  |  |  |         y, | 
					
						
							|  |  |  |         button, | 
					
						
							|  |  |  |         clickCount, | 
					
						
							|  |  |  |         modifiers, | 
					
						
							|  |  |  |         false /*aIgnoreRootScrollFrame*/, | 
					
						
							|  |  |  |         undefined /*pressure*/, | 
					
						
							|  |  |  |         undefined /*inputSource*/, | 
					
						
							|  |  |  |         undefined /*isDOMEventSynthesized*/, | 
					
						
							|  |  |  |         undefined /*isWidgetEventSynthesized*/, | 
					
						
							|  |  |  |         buttons); | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |       obs.removeObserver(trapDrag, 'on-datatransfer-available'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (type === 'mousedown' && button === 2) { | 
					
						
							|  |  |  |         frame.domWindow().windowUtils.sendMouseEvent( | 
					
						
							|  |  |  |           'contextmenu', | 
					
						
							|  |  |  |           x, | 
					
						
							|  |  |  |           y, | 
					
						
							|  |  |  |           button, | 
					
						
							|  |  |  |           clickCount, | 
					
						
							|  |  |  |           modifiers, | 
					
						
							|  |  |  |           false /*aIgnoreRootScrollFrame*/, | 
					
						
							|  |  |  |           undefined /*pressure*/, | 
					
						
							|  |  |  |           undefined /*inputSource*/, | 
					
						
							|  |  |  |           undefined /*isDOMEventSynthesized*/, | 
					
						
							|  |  |  |           undefined /*isWidgetEventSynthesized*/, | 
					
						
							|  |  |  |           buttons); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // update drag state
 | 
					
						
							| 
									
										
										
										
											2020-11-25 03:47:34 -08:00
										 |  |  |     if (this._dragging) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       if (type === 'mousemove') | 
					
						
							|  |  |  |         this._simulateDragEvent('dragover', x, y, modifiers); | 
					
						
							|  |  |  |       else if (type === 'mouseup') // firefox will do drops when any mouse button is released
 | 
					
						
							|  |  |  |         this._simulateDragEvent('drop', x, y, modifiers); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       this._cancelDragIfNeeded(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 11:27:12 -04:00
										 |  |  |   async _dispatchWheelEvent({x, y, button, deltaX, deltaY, deltaZ, modifiers }) { | 
					
						
							|  |  |  |     const deltaMode = 0; // WheelEvent.DOM_DELTA_PIXEL
 | 
					
						
							|  |  |  |     const lineOrPageDeltaX = deltaX > 0 ? Math.floor(deltaX) : Math.ceil(deltaX); | 
					
						
							|  |  |  |     const lineOrPageDeltaY = deltaY > 0 ? Math.floor(deltaY) : Math.ceil(deltaY); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const frame = this._frameTree.mainFrame(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     frame.domWindow().windowUtils.sendWheelEvent( | 
					
						
							|  |  |  |       x, | 
					
						
							|  |  |  |       y, | 
					
						
							|  |  |  |       deltaX, | 
					
						
							|  |  |  |       deltaY, | 
					
						
							|  |  |  |       deltaZ, | 
					
						
							|  |  |  |       deltaMode, | 
					
						
							|  |  |  |       modifiers, | 
					
						
							|  |  |  |       lineOrPageDeltaX, | 
					
						
							|  |  |  |       lineOrPageDeltaY, | 
					
						
							|  |  |  |       0 /* options */); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   async _insertText({text}) { | 
					
						
							|  |  |  |     const frame = this._frameTree.mainFrame(); | 
					
						
							|  |  |  |     frame.textInputProcessor().commitCompositionWith(text); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _crash() { | 
					
						
							|  |  |  |     dump(`Crashing intentionally\n`); | 
					
						
							|  |  |  |     // This is to intentionally crash the frame.
 | 
					
						
							|  |  |  |     // We crash by using js-ctypes and dereferencing
 | 
					
						
							|  |  |  |     // a bad pointer. The crash should happen immediately
 | 
					
						
							|  |  |  |     // upon loading this frame script.
 | 
					
						
							|  |  |  |     const { ctypes } = ChromeUtils.import('resource://gre/modules/ctypes.jsm'); | 
					
						
							|  |  |  |     ChromeUtils.privateNoteIntentionalCrash(); | 
					
						
							|  |  |  |     const zero = new ctypes.intptr_t(8); | 
					
						
							|  |  |  |     const badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t)); | 
					
						
							|  |  |  |     badptr.contents; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _getFullAXTree({objectId}) { | 
					
						
							|  |  |  |     let unsafeObject = null; | 
					
						
							|  |  |  |     if (objectId) { | 
					
						
							| 
									
										
										
										
											2021-04-29 22:09:10 +00:00
										 |  |  |       unsafeObject = this._frameTree.mainFrame().unsafeObject(objectId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       if (!unsafeObject) | 
					
						
							|  |  |  |         throw new Error(`No object found for id "${objectId}"`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const service = Cc["@mozilla.org/accessibilityService;1"] | 
					
						
							|  |  |  |       .getService(Ci.nsIAccessibilityService); | 
					
						
							|  |  |  |     const document = this._frameTree.mainFrame().domWindow().document; | 
					
						
							|  |  |  |     const docAcc = service.getAccessibleFor(document); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (docAcc.document.isUpdatePendingForJugglerAccessibility) | 
					
						
							|  |  |  |       await new Promise(x => this._frameTree.mainFrame().domWindow().requestAnimationFrame(x)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function waitForQuiet() { | 
					
						
							|  |  |  |       let state = {}; | 
					
						
							|  |  |  |       docAcc.getState(state, {}); | 
					
						
							|  |  |  |       if ((state.value & Ci.nsIAccessibleStates.STATE_BUSY) == 0) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       let resolve, reject; | 
					
						
							|  |  |  |       const promise = new Promise((x, y) => {resolve = x, reject = y}); | 
					
						
							|  |  |  |       let eventObserver = { | 
					
						
							|  |  |  |         observe(subject, topic) { | 
					
						
							|  |  |  |           if (topic !== "accessible-event") { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // If event type does not match expected type, skip the event.
 | 
					
						
							|  |  |  |           let event = subject.QueryInterface(Ci.nsIAccessibleEvent); | 
					
						
							|  |  |  |           if (event.eventType !== Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // If event's accessible does not match expected accessible,
 | 
					
						
							|  |  |  |           // skip the event.
 | 
					
						
							|  |  |  |           if (event.accessible !== docAcc) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           Services.obs.removeObserver(this, "accessible-event"); | 
					
						
							|  |  |  |           resolve(); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       Services.obs.addObserver(eventObserver, "accessible-event"); | 
					
						
							|  |  |  |       return promise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     function buildNode(accElement) { | 
					
						
							|  |  |  |       let a = {}, b = {}; | 
					
						
							|  |  |  |       accElement.getState(a, b); | 
					
						
							|  |  |  |       const tree = { | 
					
						
							|  |  |  |         role: service.getStringRole(accElement.role), | 
					
						
							|  |  |  |         name: accElement.name || '', | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       if (unsafeObject && unsafeObject === accElement.DOMNode) | 
					
						
							|  |  |  |         tree.foundObject = true; | 
					
						
							|  |  |  |       for (const userStringProperty of [ | 
					
						
							|  |  |  |         'value', | 
					
						
							|  |  |  |         'description' | 
					
						
							|  |  |  |       ]) { | 
					
						
							|  |  |  |         tree[userStringProperty] = accElement[userStringProperty] || undefined; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const states = {}; | 
					
						
							|  |  |  |       for (const name of service.getStringStates(a.value, b.value)) | 
					
						
							|  |  |  |         states[name] = true; | 
					
						
							|  |  |  |       for (const name of ['selected', | 
					
						
							|  |  |  |         'focused', | 
					
						
							|  |  |  |         'pressed', | 
					
						
							|  |  |  |         'focusable', | 
					
						
							|  |  |  |         'haspopup', | 
					
						
							|  |  |  |         'required', | 
					
						
							|  |  |  |         'invalid', | 
					
						
							|  |  |  |         'modal', | 
					
						
							|  |  |  |         'editable', | 
					
						
							|  |  |  |         'busy', | 
					
						
							|  |  |  |         'checked', | 
					
						
							|  |  |  |         'multiselectable']) { | 
					
						
							|  |  |  |         if (states[name]) | 
					
						
							|  |  |  |           tree[name] = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (states['multi line']) | 
					
						
							|  |  |  |         tree['multiline'] = true; | 
					
						
							|  |  |  |       if (states['editable'] && states['readonly']) | 
					
						
							|  |  |  |         tree['readonly'] = true; | 
					
						
							|  |  |  |       if (states['checked']) | 
					
						
							|  |  |  |         tree['checked'] = true; | 
					
						
							|  |  |  |       if (states['mixed']) | 
					
						
							|  |  |  |         tree['checked'] = 'mixed'; | 
					
						
							|  |  |  |       if (states['expanded']) | 
					
						
							|  |  |  |         tree['expanded'] = true; | 
					
						
							|  |  |  |       else if (states['collapsed']) | 
					
						
							|  |  |  |         tree['expanded'] = false; | 
					
						
							|  |  |  |       if (!states['enabled']) | 
					
						
							|  |  |  |         tree['disabled'] = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const attributes = {}; | 
					
						
							|  |  |  |       if (accElement.attributes) { | 
					
						
							|  |  |  |         for (const { key, value } of accElement.attributes.enumerate()) { | 
					
						
							|  |  |  |           attributes[key] = value; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       for (const numericalProperty of ['level']) { | 
					
						
							|  |  |  |         if (numericalProperty in attributes) | 
					
						
							|  |  |  |           tree[numericalProperty] = parseFloat(attributes[numericalProperty]); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       for (const stringProperty of ['tag', 'roledescription', 'valuetext', 'orientation', 'autocomplete', 'keyshortcuts']) { | 
					
						
							|  |  |  |         if (stringProperty in attributes) | 
					
						
							|  |  |  |           tree[stringProperty] = attributes[stringProperty]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const children = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       for (let child = accElement.firstChild; child; child = child.nextSibling) { | 
					
						
							|  |  |  |         children.push(buildNode(child)); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (children.length) | 
					
						
							|  |  |  |         tree.children = children; | 
					
						
							|  |  |  |       return tree; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     await waitForQuiet(); | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       tree: buildNode(docAcc) | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 14:51:02 -07:00
										 |  |  | function takeScreenshot(win, left, top, width, height, mimeType, omitDeviceScaleFactor) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   const MAX_SKIA_DIMENSIONS = 32767; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 14:51:02 -07:00
										 |  |  |   const scale = omitDeviceScaleFactor ? 1 : win.devicePixelRatio; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   const canvasWidth = width * scale; | 
					
						
							|  |  |  |   const canvasHeight = height * scale; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (canvasWidth > MAX_SKIA_DIMENSIONS || canvasHeight > MAX_SKIA_DIMENSIONS) | 
					
						
							|  |  |  |     throw new Error('Cannot take screenshot larger than ' + MAX_SKIA_DIMENSIONS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const canvas = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); | 
					
						
							|  |  |  |   canvas.width = canvasWidth; | 
					
						
							|  |  |  |   canvas.height = canvasHeight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let ctx = canvas.getContext('2d'); | 
					
						
							|  |  |  |   ctx.scale(scale, scale); | 
					
						
							|  |  |  |   ctx.drawWindow(win, left, top, width, height, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_CARET); | 
					
						
							|  |  |  |   const dataURL = canvas.toDataURL(mimeType); | 
					
						
							|  |  |  |   return dataURL.substring(dataURL.indexOf(',') + 1); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var EXPORTED_SYMBOLS = ['PageAgent']; | 
					
						
							|  |  |  | this.PageAgent = PageAgent; | 
					
						
							|  |  |  | 
 |