Best JavaScript code snippet using playwright-internal
TargetRegistry.js
Source:TargetRegistry.js
1/* This Source Code Form is subject to the terms of the Mozilla Public2 * License, v. 2.0. If a copy of the MPL was not distributed with this3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */4const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');5const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');6const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');7const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");8const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");9const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");10const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');11const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");12const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");13const helper = new Helper();14const IDENTITY_NAME = 'JUGGLER ';15const HUNDRED_YEARS = 60 * 60 * 24 * 365 * 100;16const ALL_PERMISSIONS = [17 'geo',18 'desktop-notification',19];20class DownloadInterceptor {21 constructor(registry) {22 this._registry = registry23 this._handlerToUuid = new Map();24 }25 //26 // nsIDownloadInterceptor implementation.27 //28 interceptDownloadRequest(externalAppHandler, request, browsingContext, outFile) {29 if (!(request instanceof Ci.nsIChannel))30 return false;31 const channel = request.QueryInterface(Ci.nsIChannel);32 let pageTarget = this._registry._browserBrowsingContextToTarget.get(channel.loadInfo.browsingContext);33 if (!pageTarget)34 return false;35 const browserContext = pageTarget.browserContext();36 const options = browserContext.downloadOptions;37 if (!options)38 return false;39 const uuid = helper.generateId();40 let file = null;41 if (options.behavior === 'saveToDisk') {42 file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);43 file.initWithPath(options.downloadsDir);44 file.append(uuid);45 try {46 file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);47 } catch (e) {48 dump(`interceptDownloadRequest failed to create file: ${e}\n`);49 return false;50 }51 }52 outFile.value = file;53 this._handlerToUuid.set(externalAppHandler, uuid);54 const downloadInfo = {55 uuid,56 browserContextId: browserContext.browserContextId,57 pageTargetId: pageTarget.id(),58 url: request.name,59 suggestedFileName: externalAppHandler.suggestedFileName,60 };61 this._registry.emit(TargetRegistry.Events.DownloadCreated, downloadInfo);62 return true;63 }64 onDownloadComplete(externalAppHandler, canceled, errorName) {65 const uuid = this._handlerToUuid.get(externalAppHandler);66 if (!uuid)67 return;68 this._handlerToUuid.delete(externalAppHandler);69 const downloadInfo = {70 uuid,71 };72 if (errorName === 'NS_BINDING_ABORTED') {73 downloadInfo.canceled = true;74 } else {75 downloadInfo.error = errorName;76 }77 this._registry.emit(TargetRegistry.Events.DownloadFinished, downloadInfo);78 }79}80class TargetRegistry {81 constructor() {82 EventEmitter.decorate(this);83 this._browserContextIdToBrowserContext = new Map();84 this._userContextIdToBrowserContext = new Map();85 this._browserToTarget = new Map();86 this._browserBrowsingContextToTarget = new Map();87 this._browserProxy = null;88 // Cleanup containers from previous runs (if any)89 for (const identity of ContextualIdentityService.getPublicIdentities()) {90 if (identity.name && identity.name.startsWith(IDENTITY_NAME)) {91 ContextualIdentityService.remove(identity.userContextId);92 ContextualIdentityService.closeContainerTabs(identity.userContextId);93 }94 }95 this._defaultContext = new BrowserContext(this, undefined, undefined);96 Services.obs.addObserver({97 observe: (subject, topic, data) => {98 const browser = subject.ownerElement;99 if (!browser)100 return;101 const target = this._browserToTarget.get(browser);102 if (!target)103 return;104 target.emit(PageTarget.Events.Crashed);105 target.dispose();106 }107 }, 'oop-frameloader-crashed');108 Services.mm.addMessageListener('juggler:content-ready', {109 receiveMessage: message => {110 const linkedBrowser = message.target;111 const target = this._browserToTarget.get(linkedBrowser);112 if (!target)113 return;114 return {115 scriptsToEvaluateOnNewDocument: target.browserContext().scriptsToEvaluateOnNewDocument,116 bindings: target.browserContext().bindings,117 settings: target.browserContext().settings,118 };119 },120 });121 const onTabOpenListener = (appWindow, window, event) => {122 const tab = event.target;123 const userContextId = tab.userContextId;124 const browserContext = this._userContextIdToBrowserContext.get(userContextId);125 const hasExplicitSize = appWindow && (appWindow.chromeFlags & Ci.nsIWebBrowserChrome.JUGGLER_WINDOW_EXPLICIT_SIZE) !== 0;126 const openerContext = tab.linkedBrowser.browsingContext.opener;127 let openerTarget;128 if (openerContext) {129 // Popups usually have opener context.130 openerTarget = this._browserBrowsingContextToTarget.get(openerContext);131 } else if (tab.openerTab) {132 // Noopener popups from the same window have opener tab instead.133 openerTarget = this._browserToTarget.get(tab.openerTab.linkedBrowser);134 }135 if (!browserContext)136 throw new Error(`Internal error: cannot find context for userContextId=${userContextId}`);137 const target = new PageTarget(this, window, tab, browserContext, openerTarget);138 target.updateUserAgent();139 target.updateTouchOverride();140 target.updateColorSchemeOverride();141 if (!hasExplicitSize)142 target.updateViewportSize();143 if (browserContext.screencastOptions)144 target._startVideoRecording(browserContext.screencastOptions);145 };146 const onTabCloseListener = event => {147 const tab = event.target;148 const linkedBrowser = tab.linkedBrowser;149 const target = this._browserToTarget.get(linkedBrowser);150 if (target)151 target.dispose();152 };153 const domWindowTabListeners = new Map();154 const onOpenWindow = async (appWindow) => {155 let domWindow;156 if (appWindow instanceof Ci.nsIAppWindow) {157 domWindow = appWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);158 } else {159 domWindow = appWindow;160 appWindow = null;161 }162 if (!(domWindow instanceof Ci.nsIDOMChromeWindow))163 return;164 // In persistent mode, window might be opened long ago and might be165 // already initialized.166 //167 // In this case, we want to keep this callback synchronous so that we will call168 // `onTabOpenListener` synchronously and before the sync IPc message `juggler:content-ready`.169 if (domWindow.document.readyState === 'uninitialized' || domWindow.document.readyState === 'loading') {170 // For non-initialized windows, DOMContentLoaded initializes gBrowser171 // and starts tab loading (see //browser/base/content/browser.js), so we172 // are guaranteed to call `onTabOpenListener` before the sync IPC message173 // `juggler:content-ready`.174 await helper.awaitEvent(domWindow, 'DOMContentLoaded');175 }176 if (!domWindow.gBrowser)177 return;178 const tabContainer = domWindow.gBrowser.tabContainer;179 domWindowTabListeners.set(domWindow, [180 helper.addEventListener(tabContainer, 'TabOpen', event => onTabOpenListener(appWindow, domWindow, event)),181 helper.addEventListener(tabContainer, 'TabClose', onTabCloseListener),182 ]);183 for (const tab of domWindow.gBrowser.tabs)184 onTabOpenListener(appWindow, domWindow, { target: tab });185 };186 const onCloseWindow = window => {187 const domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);188 if (!(domWindow instanceof Ci.nsIDOMChromeWindow))189 return;190 if (!domWindow.gBrowser)191 return;192 const listeners = domWindowTabListeners.get(domWindow) || [];193 domWindowTabListeners.delete(domWindow);194 helper.removeListeners(listeners);195 for (const tab of domWindow.gBrowser.tabs)196 onTabCloseListener({ target: tab });197 };198 const extHelperAppSvc = Cc["@mozilla.org/uriloader/external-helper-app-service;1"].getService(Ci.nsIExternalHelperAppService);199 extHelperAppSvc.setDownloadInterceptor(new DownloadInterceptor(this));200 Services.wm.addListener({ onOpenWindow, onCloseWindow });201 for (const win of Services.wm.getEnumerator(null))202 onOpenWindow(win);203 }204 setBrowserProxy(proxy) {205 this._browserProxy = proxy;206 }207 getProxyInfo(channel) {208 const originAttributes = channel.loadInfo && channel.loadInfo.originAttributes;209 const browserContext = originAttributes ? this.browserContextForUserContextId(originAttributes.userContextId) : null;210 // Prefer context proxy and fallback to browser-level proxy.211 const proxyInfo = (browserContext && browserContext._proxy) || this._browserProxy;212 if (!proxyInfo || proxyInfo.bypass.some(domainSuffix => channel.URI.host.endsWith(domainSuffix)))213 return null;214 return proxyInfo;215 }216 defaultContext() {217 return this._defaultContext;218 }219 createBrowserContext(removeOnDetach) {220 return new BrowserContext(this, helper.generateId(), removeOnDetach);221 }222 browserContextForId(browserContextId) {223 return this._browserContextIdToBrowserContext.get(browserContextId);224 }225 browserContextForUserContextId(userContextId) {226 return this._userContextIdToBrowserContext.get(userContextId);227 }228 async newPage({browserContextId}) {229 const browserContext = this.browserContextForId(browserContextId);230 const features = "chrome,dialog=no,all";231 // See _callWithURIToLoad in browser.js for the structure of window.arguments232 // window.arguments[1]: unused (bug 871161)233 // [2]: referrerInfo (nsIReferrerInfo)234 // [3]: postData (nsIInputStream)235 // [4]: allowThirdPartyFixup (bool)236 // [5]: userContextId (int)237 // [6]: originPrincipal (nsIPrincipal)238 // [7]: originStoragePrincipal (nsIPrincipal)239 // [8]: triggeringPrincipal (nsIPrincipal)240 // [9]: allowInheritPrincipal (bool)241 // [10]: csp (nsIContentSecurityPolicy)242 // [11]: nsOpenWindowInfo243 const args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);244 const urlSupports = Cc["@mozilla.org/supports-string;1"].createInstance(245 Ci.nsISupportsString246 );247 urlSupports.data = 'about:blank';248 args.appendElement(urlSupports); // 0249 args.appendElement(undefined); // 1250 args.appendElement(undefined); // 2251 args.appendElement(undefined); // 3252 args.appendElement(undefined); // 4253 const userContextIdSupports = Cc[254 "@mozilla.org/supports-PRUint32;1"255 ].createInstance(Ci.nsISupportsPRUint32);256 userContextIdSupports.data = browserContext.userContextId;257 args.appendElement(userContextIdSupports); // 5258 args.appendElement(undefined); // 6259 args.appendElement(undefined); // 7260 args.appendElement(Services.scriptSecurityManager.getSystemPrincipal()); // 8261 const window = Services.ww.openWindow(null, AppConstants.BROWSER_CHROME_URL, '_blank', features, args);262 await waitForWindowReady(window);263 if (window.gBrowser.browsers.length !== 1)264 throw new Error(`Unexpected number of tabs in the new window: ${window.gBrowser.browsers.length}`);265 const browser = window.gBrowser.browsers[0];266 const target = this._browserToTarget.get(browser);267 browser.focus();268 if (browserContext.settings.timezoneId) {269 if (await target.hasFailedToOverrideTimezone())270 throw new Error('Failed to override timezone');271 }272 return target.id();273 }274 targets() {275 return Array.from(this._browserToTarget.values());276 }277 targetForBrowser(browser) {278 return this._browserToTarget.get(browser);279 }280}281class PageTarget {282 constructor(registry, win, tab, browserContext, opener) {283 EventEmitter.decorate(this);284 this._targetId = helper.generateId();285 this._registry = registry;286 this._window = win;287 this._gBrowser = win.gBrowser;288 this._tab = tab;289 this._linkedBrowser = tab.linkedBrowser;290 this._browserContext = browserContext;291 this._viewportSize = undefined;292 this._initialDPPX = this._linkedBrowser.browsingContext.overrideDPPX;293 this._url = 'about:blank';294 this._openerId = opener ? opener.id() : undefined;295 this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, this._linkedBrowser.messageManager);296 this._screencastInfo = undefined;297 this._dialogs = new Map();298 const navigationListener = {299 QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),300 onLocationChange: (aWebProgress, aRequest, aLocation) => this._onNavigated(aLocation),301 };302 this._eventListeners = [303 helper.addObserver(this._updateModalDialogs.bind(this), 'tabmodal-dialog-loaded'),304 helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION),305 helper.addEventListener(this._linkedBrowser, 'DOMModalDialogClosed', event => this._updateModalDialogs()),306 ];307 this._disposed = false;308 browserContext.pages.add(this);309 this._registry._browserToTarget.set(this._linkedBrowser, this);310 this._registry._browserBrowsingContextToTarget.set(this._linkedBrowser.browsingContext, this);311 this._registry.emit(TargetRegistry.Events.TargetCreated, this);312 }313 dialog(dialogId) {314 return this._dialogs.get(dialogId);315 }316 dialogs() {317 return [...this._dialogs.values()];318 }319 async windowReady() {320 await waitForWindowReady(this._window);321 }322 linkedBrowser() {323 return this._linkedBrowser;324 }325 browserContext() {326 return this._browserContext;327 }328 updateTouchOverride() {329 this._linkedBrowser.browsingContext.touchEventsOverride = this._browserContext.touchOverride ? 'enabled' : 'none';330 }331 updateUserAgent() {332 this._linkedBrowser.browsingContext.customUserAgent = this._browserContext.defaultUserAgent;333 }334 _updateModalDialogs() {335 const prompts = new Set(this._linkedBrowser.tabModalPromptBox ? this._linkedBrowser.tabModalPromptBox.listPrompts() : []);336 for (const dialog of this._dialogs.values()) {337 if (!prompts.has(dialog.prompt())) {338 this._dialogs.delete(dialog.id());339 this.emit(PageTarget.Events.DialogClosed, dialog);340 } else {341 prompts.delete(dialog.prompt());342 }343 }344 for (const prompt of prompts) {345 const dialog = Dialog.createIfSupported(prompt);346 if (!dialog)347 continue;348 this._dialogs.set(dialog.id(), dialog);349 this.emit(PageTarget.Events.DialogOpened, dialog);350 }351 }352 async updateViewportSize() {353 // Viewport size is defined by three arguments:354 // 1. default size. Could be explicit if set as part of `window.open` call, e.g.355 // `window.open(url, title, 'width=400,height=400')`356 // 2. page viewport size357 // 3. browserContext viewport size358 //359 // The "default size" (1) is only respected when the page is opened.360 // Otherwise, explicitly set page viewport prevales over browser context361 // default viewport.362 const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;363 const actualSize = await setViewportSizeForBrowser(viewportSize, this._linkedBrowser, this._window);364 this._linkedBrowser.browsingContext.overrideDPPX = this._browserContext.deviceScaleFactor || this._initialDPPX;365 await this._channel.connect('').send('awaitViewportDimensions', {366 width: actualSize.width,367 height: actualSize.height,368 deviceSizeIsPageSize: !!this._browserContext.deviceScaleFactor,369 });370 }371 setEmulatedMedia(mediumOverride) {372 this._linkedBrowser.browsingContext.mediumOverride = mediumOverride || '';373 }374 setColorScheme(colorScheme) {375 this.colorScheme = fromProtocolColorScheme(colorScheme);376 this.updateColorSchemeOverride();377 }378 updateColorSchemeOverride() {379 this._linkedBrowser.browsingContext.prefersColorSchemeOverride = this.colorScheme || this._browserContext.colorScheme || 'none';380 }381 async setViewportSize(viewportSize) {382 this._viewportSize = viewportSize;383 await this.updateViewportSize();384 }385 close(runBeforeUnload = false) {386 this._gBrowser.removeTab(this._tab, {387 skipPermitUnload: !runBeforeUnload,388 });389 }390 channel() {391 return this._channel;392 }393 id() {394 return this._targetId;395 }396 info() {397 return {398 targetId: this.id(),399 type: 'page',400 browserContextId: this._browserContext.browserContextId,401 openerId: this._openerId,402 };403 }404 _onNavigated(aLocation) {405 this._url = aLocation.spec;406 this._browserContext.grantPermissionsToOrigin(this._url);407 }408 async ensurePermissions() {409 await this._channel.connect('').send('ensurePermissions', {}).catch(e => void e);410 }411 async addScriptToEvaluateOnNewDocument(script) {412 await this._channel.connect('').send('addScriptToEvaluateOnNewDocument', script).catch(e => void e);413 }414 async addBinding(name, script) {415 await this._channel.connect('').send('addBinding', { name, script }).catch(e => void e);416 }417 async applyContextSetting(name, value) {418 await this._channel.connect('').send('applyContextSetting', { name, value }).catch(e => void e);419 }420 async hasFailedToOverrideTimezone() {421 return await this._channel.connect('').send('hasFailedToOverrideTimezone').catch(e => true);422 }423 async _startVideoRecording({width, height, scale, dir}) {424 // On Mac the window may not yet be visible when TargetCreated and its425 // NSWindow.windowNumber may be -1, so we wait until the window is known426 // to be initialized and visible.427 await this.windowReady();428 const file = OS.Path.join(dir, helper.generateId() + '.webm');429 if (width < 10 || width > 10000 || height < 10 || height > 10000)430 throw new Error("Invalid size");431 if (scale && (scale <= 0 || scale > 1))432 throw new Error("Unsupported scale");433 const screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);434 const docShell = this._gBrowser.ownerGlobal.docShell;435 // Exclude address bar and navigation control from the video.436 const rect = this.linkedBrowser().getBoundingClientRect();437 const devicePixelRatio = this._window.devicePixelRatio;438 const videoSessionId = screencast.startVideoRecording(docShell, file, width, height, scale || 0, devicePixelRatio * rect.top);439 this._screencastInfo = { videoSessionId, file };440 this.emit(PageTarget.Events.ScreencastStarted);441 }442 async _stopVideoRecording() {443 if (!this._screencastInfo)444 throw new Error('No video recording in progress');445 const screencastInfo = this._screencastInfo;446 this._screencastInfo = undefined;447 const screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);448 const result = new Promise(resolve =>449 Services.obs.addObserver(function onStopped(subject, topic, data) {450 if (screencastInfo.videoSessionId != data)451 return;452 Services.obs.removeObserver(onStopped, 'juggler-screencast-stopped');453 resolve();454 }, 'juggler-screencast-stopped')455 );456 screencast.stopVideoRecording(screencastInfo.videoSessionId);457 return result;458 }459 screencastInfo() {460 return this._screencastInfo;461 }462 dispose() {463 this._disposed = true;464 if (this._screencastInfo)465 this._stopVideoRecording().catch(e => dump(`stopVideoRecording failed:\n${e}\n`));466 this._browserContext.pages.delete(this);467 this._registry._browserToTarget.delete(this._linkedBrowser);468 this._registry._browserBrowsingContextToTarget.delete(this._linkedBrowser.browsingContext);469 try {470 helper.removeListeners(this._eventListeners);471 } catch (e) {472 // In some cases, removing listeners from this._linkedBrowser fails473 // because it is already half-destroyed.474 if (e)475 dump(e.message + '\n' + e.stack + '\n');476 }477 this._registry.emit(TargetRegistry.Events.TargetDestroyed, this);478 }479}480PageTarget.Events = {481 ScreencastStarted: Symbol('PageTarget.ScreencastStarted'),482 Crashed: Symbol('PageTarget.Crashed'),483 DialogOpened: Symbol('PageTarget.DialogOpened'),484 DialogClosed: Symbol('PageTarget.DialogClosed'),485};486function fromProtocolColorScheme(colorScheme) {487 if (colorScheme === 'light' || colorScheme === 'dark')488 return colorScheme;489 if (colorScheme === null || colorScheme === 'no-preference')490 return undefined;491 throw new Error('Unknown color scheme: ' + colorScheme);492}493class BrowserContext {494 constructor(registry, browserContextId, removeOnDetach) {495 this._registry = registry;496 this.browserContextId = browserContextId;497 // Default context has userContextId === 0, but we pass undefined to many APIs just in case.498 this.userContextId = 0;499 if (browserContextId !== undefined) {500 const identity = ContextualIdentityService.create(IDENTITY_NAME + browserContextId);501 this.userContextId = identity.userContextId;502 }503 this._principals = [];504 // Maps origins to the permission lists.505 this._permissions = new Map();506 this._registry._browserContextIdToBrowserContext.set(this.browserContextId, this);507 this._registry._userContextIdToBrowserContext.set(this.userContextId, this);508 this._proxy = null;509 this.removeOnDetach = removeOnDetach;510 this.extraHTTPHeaders = undefined;511 this.httpCredentials = undefined;512 this.requestInterceptionEnabled = undefined;513 this.ignoreHTTPSErrors = undefined;514 this.downloadOptions = undefined;515 this.defaultViewportSize = undefined;516 this.deviceScaleFactor = undefined;517 this.defaultUserAgent = null;518 this.touchOverride = false;519 this.colorScheme = 'none';520 this.screencastOptions = undefined;521 this.scriptsToEvaluateOnNewDocument = [];522 this.bindings = [];523 this.settings = {};524 this.pages = new Set();525 }526 setColorScheme(colorScheme) {527 this.colorScheme = fromProtocolColorScheme(colorScheme);528 for (const page of this.pages)529 page.updateColorSchemeOverride();530 }531 async destroy() {532 if (this.userContextId !== 0) {533 ContextualIdentityService.remove(this.userContextId);534 for (const page of this.pages)535 page.close();536 if (this.pages.size) {537 await new Promise(f => {538 const listener = helper.on(this._registry, TargetRegistry.Events.TargetDestroyed, () => {539 if (!this.pages.size) {540 helper.removeListeners([listener]);541 f();542 }543 });544 });545 }546 }547 this._registry._browserContextIdToBrowserContext.delete(this.browserContextId);548 this._registry._userContextIdToBrowserContext.delete(this.userContextId);549 }550 setProxy(proxy) {551 // Clear AuthCache.552 Services.obs.notifyObservers(null, "net:clear-active-logins");553 this._proxy = proxy;554 }555 setIgnoreHTTPSErrors(ignoreHTTPSErrors) {556 if (this.ignoreHTTPSErrors === ignoreHTTPSErrors)557 return;558 this.ignoreHTTPSErrors = ignoreHTTPSErrors;559 const certOverrideService = Cc[560 "@mozilla.org/security/certoverride;1"561 ].getService(Ci.nsICertOverrideService);562 if (ignoreHTTPSErrors) {563 Preferences.set("network.stricttransportsecurity.preloadlist", false);564 Preferences.set("security.cert_pinning.enforcement_level", 0);565 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(true, this.userContextId);566 } else {567 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(false, this.userContextId);568 }569 }570 async setDefaultUserAgent(userAgent) {571 this.defaultUserAgent = userAgent;572 for (const page of this.pages)573 page.updateUserAgent();574 }575 setTouchOverride(touchOverride) {576 this.touchOverride = touchOverride;577 for (const page of this.pages)578 page.updateTouchOverride();579 }580 async setDefaultViewport(viewport) {581 this.defaultViewportSize = viewport ? viewport.viewportSize : undefined;582 this.deviceScaleFactor = viewport ? viewport.deviceScaleFactor : undefined;583 await Promise.all(Array.from(this.pages).map(page => page.updateViewportSize()));584 }585 async addScriptToEvaluateOnNewDocument(script) {586 this.scriptsToEvaluateOnNewDocument.push(script);587 await Promise.all(Array.from(this.pages).map(page => page.addScriptToEvaluateOnNewDocument(script)));588 }589 async addBinding(name, script) {590 this.bindings.push({ name, script });591 await Promise.all(Array.from(this.pages).map(page => page.addBinding(name, script)));592 }593 async applySetting(name, value) {594 this.settings[name] = value;595 await Promise.all(Array.from(this.pages).map(page => page.applyContextSetting(name, value)));596 }597 async grantPermissions(origin, permissions) {598 this._permissions.set(origin, permissions);599 const promises = [];600 for (const page of this.pages) {601 if (origin === '*' || page._url.startsWith(origin)) {602 this.grantPermissionsToOrigin(page._url);603 promises.push(page.ensurePermissions());604 }605 }606 await Promise.all(promises);607 }608 resetPermissions() {609 for (const principal of this._principals) {610 for (const permission of ALL_PERMISSIONS)611 Services.perms.removeFromPrincipal(principal, permission);612 }613 this._principals = [];614 this._permissions.clear();615 }616 grantPermissionsToOrigin(url) {617 let origin = Array.from(this._permissions.keys()).find(key => url.startsWith(key));618 if (!origin)619 origin = '*';620 const permissions = this._permissions.get(origin);621 if (!permissions)622 return;623 const attrs = { userContextId: this.userContextId || undefined };624 const principal = Services.scriptSecurityManager.createContentPrincipal(NetUtil.newURI(url), attrs);625 this._principals.push(principal);626 for (const permission of ALL_PERMISSIONS) {627 const action = permissions.includes(permission) ? Ci.nsIPermissionManager.ALLOW_ACTION : Ci.nsIPermissionManager.DENY_ACTION;628 Services.perms.addFromPrincipal(principal, permission, action, Ci.nsIPermissionManager.EXPIRE_NEVER, 0 /* expireTime */);629 }630 }631 setCookies(cookies) {632 const protocolToSameSite = {633 [undefined]: Ci.nsICookie.SAMESITE_NONE,634 'Lax': Ci.nsICookie.SAMESITE_LAX,635 'Strict': Ci.nsICookie.SAMESITE_STRICT,636 };637 for (const cookie of cookies) {638 const uri = cookie.url ? NetUtil.newURI(cookie.url) : null;639 let domain = cookie.domain;640 if (!domain) {641 if (!uri)642 throw new Error('At least one of the url and domain needs to be specified');643 domain = uri.host;644 }645 let path = cookie.path;646 if (!path)647 path = uri ? dirPath(uri.filePath) : '/';648 let secure = false;649 if (cookie.secure !== undefined)650 secure = cookie.secure;651 else if (uri && uri.scheme === 'https')652 secure = true;653 Services.cookies.add(654 domain,655 path,656 cookie.name,657 cookie.value,658 secure,659 cookie.httpOnly || false,660 cookie.expires === undefined || cookie.expires === -1 /* isSession */,661 cookie.expires === undefined ? Date.now() + HUNDRED_YEARS : cookie.expires,662 { userContextId: this.userContextId || undefined } /* originAttributes */,663 protocolToSameSite[cookie.sameSite],664 Ci.nsICookie.SCHEME_UNSET665 );666 }667 }668 clearCookies() {669 Services.cookies.removeCookiesWithOriginAttributes(JSON.stringify({ userContextId: this.userContextId || undefined }));670 }671 getCookies() {672 const result = [];673 const sameSiteToProtocol = {674 [Ci.nsICookie.SAMESITE_NONE]: 'None',675 [Ci.nsICookie.SAMESITE_LAX]: 'Lax',676 [Ci.nsICookie.SAMESITE_STRICT]: 'Strict',677 };678 for (let cookie of Services.cookies.cookies) {679 if (cookie.originAttributes.userContextId !== this.userContextId)680 continue;681 if (cookie.host === 'addons.mozilla.org')682 continue;683 result.push({684 name: cookie.name,685 value: cookie.value,686 domain: cookie.host,687 path: cookie.path,688 expires: cookie.isSession ? -1 : cookie.expiry,689 size: cookie.name.length + cookie.value.length,690 httpOnly: cookie.isHttpOnly,691 secure: cookie.isSecure,692 session: cookie.isSession,693 sameSite: sameSiteToProtocol[cookie.sameSite],694 });695 }696 return result;697 }698 async setScreencastOptions(options) {699 this.screencastOptions = options;700 if (!options)701 return;702 const promises = [];703 for (const page of this.pages)704 promises.push(page._startVideoRecording(options));705 await Promise.all(promises);706 }707}708class Dialog {709 static createIfSupported(prompt) {710 const type = prompt.args.promptType;711 switch (type) {712 case 'alert':713 case 'alertCheck':714 return new Dialog(prompt, 'alert');715 case 'prompt':716 return new Dialog(prompt, 'prompt');717 case 'confirm':718 case 'confirmCheck':719 return new Dialog(prompt, 'confirm');720 case 'confirmEx':721 return new Dialog(prompt, 'beforeunload');722 default:723 return null;724 };725 }726 constructor(prompt, type) {727 this._id = helper.generateId();728 this._type = type;729 this._prompt = prompt;730 }731 id() {732 return this._id;733 }734 message() {735 return this._prompt.ui.infoBody.textContent;736 }737 type() {738 return this._type;739 }740 prompt() {741 return this._prompt;742 }743 dismiss() {744 if (this._prompt.ui.button1)745 this._prompt.ui.button1.click();746 else747 this._prompt.ui.button0.click();748 }749 defaultValue() {750 return this._prompt.ui.loginTextbox.value;751 }752 accept(promptValue) {753 if (typeof promptValue === 'string' && this._type === 'prompt')754 this._prompt.ui.loginTextbox.value = promptValue;755 this._prompt.ui.button0.click();756 }757}758function dirPath(path) {759 return path.substring(0, path.lastIndexOf('/') + 1);760}761async function waitForWindowReady(window) {762 if (window.delayedStartupPromise) {763 await window.delayedStartupPromise;764 } else {765 await new Promise((resolve => {766 Services.obs.addObserver(function observer(aSubject, aTopic) {767 if (window == aSubject) {768 Services.obs.removeObserver(observer, aTopic);769 resolve();770 }771 }, "browser-delayed-startup-finished");772 }));773 }774 if (window.document.readyState !== 'complete')775 await helper.awaitEvent(window, 'load');776}777async function setViewportSizeForBrowser(viewportSize, browser, window) {778 await waitForWindowReady(window);779 if (viewportSize) {780 const {width, height} = viewportSize;781 const rect = browser.getBoundingClientRect();782 window.resizeBy(width - rect.width, height - rect.height);783 browser.style.setProperty('min-width', width + 'px');784 browser.style.setProperty('min-height', height + 'px');785 browser.style.setProperty('max-width', width + 'px');786 browser.style.setProperty('max-height', height + 'px');787 } else {788 browser.style.removeProperty('min-width');789 browser.style.removeProperty('min-height');790 browser.style.removeProperty('max-width');791 browser.style.removeProperty('max-height');792 }793 const rect = browser.getBoundingClientRect();794 return { width: rect.width, height: rect.height };795}796TargetRegistry.Events = {797 TargetCreated: Symbol('TargetRegistry.Events.TargetCreated'),798 TargetDestroyed: Symbol('TargetRegistry.Events.TargetDestroyed'),799 DownloadCreated: Symbol('TargetRegistry.Events.DownloadCreated'),800 DownloadFinished: Symbol('TargetRegistry.Events.DownloadFinished'),801};802var EXPORTED_SYMBOLS = ['TargetRegistry', 'PageTarget'];803this.TargetRegistry = TargetRegistry;...
PageAgent.js
Source:PageAgent.js
...18 const docShell = frameTree.mainFrame().docShell();19 this._initialDPPX = docShell.contentViewer.overrideDPPX;20 this._customScrollbars = null;21 }22 async awaitViewportDimensions({width, height}) {23 const win = this._frameTree.mainFrame().domWindow();24 if (win.innerWidth === width && win.innerHeight === height)25 return;26 await new Promise(resolve => {27 const listener = helper.addEventListener(win, 'resize', () => {28 if (win.innerWidth === width && win.innerHeight === height) {29 helper.removeListeners([listener]);30 resolve();31 }32 });33 });34 }35 async setViewport({deviceScaleFactor, isMobile, hasTouch}) {36 const docShell = this._frameTree.mainFrame().docShell();...
PageHandler.jsm
Source:PageHandler.jsm
1"use strict";2const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');3const Cc = Components.classes;4const Ci = Components.interfaces;5const Cu = Components.utils;6const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';7const FRAME_SCRIPT = "chrome://juggler/content/content/ContentSession.js";8const helper = new Helper();9class PageHandler {10 constructor(chromeSession, tab) {11 this._pageId = helper.generateId();12 this._chromeSession = chromeSession;13 this._tab = tab;14 this._browser = tab.linkedBrowser;15 this._enabled = false;16 this.QueryInterface = ChromeUtils.generateQI([17 Ci.nsIWebProgressListener,18 Ci.nsISupportsWeakReference,19 ]);20 this._browser.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);21 this._dialogs = new Map();22 // First navigation always happens to about:blank - do not report it.23 this._skipNextNavigation = true;24 }25 async setViewport({viewport}) {26 if (viewport) {27 const {width, height} = viewport;28 this._browser.style.setProperty('min-width', width + 'px');29 this._browser.style.setProperty('min-height', height + 'px');30 this._browser.style.setProperty('max-width', width + 'px');31 this._browser.style.setProperty('max-height', height + 'px');32 } else {33 this._browser.style.removeProperty('min-width');34 this._browser.style.removeProperty('min-height');35 this._browser.style.removeProperty('max-width');36 this._browser.style.removeProperty('max-height');37 }38 const dimensions = this._browser.getBoundingClientRect();39 await Promise.all([40 this._contentSession.send('setViewport', {41 deviceScaleFactor: viewport ? viewport.deviceScaleFactor : 0,42 isMobile: viewport && viewport.isMobile,43 hasTouch: viewport && viewport.hasTouch,44 }),45 this._contentSession.send('awaitViewportDimensions', {46 width: dimensions.width,47 height: dimensions.height48 }),49 ]);50 }51 _initializeDialogEvents() {52 this._browser.addEventListener('DOMWillOpenModalDialog', async (event) => {53 // wait for the dialog to be actually added to DOM.54 await Promise.resolve();55 this._updateModalDialogs();56 });57 this._browser.addEventListener('DOMModalDialogClosed', (event) => {58 this._updateModalDialogs();59 });60 this._updateModalDialogs();61 }62 _updateModalDialogs() {63 const elements = new Set(this._browser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt"));64 for (const dialog of this._dialogs.values()) {65 if (!elements.has(dialog.element())) {66 this._dialogs.delete(dialog.id());67 this._chromeSession.emitEvent('Page.dialogClosed', {68 pageId: this._pageId,69 dialogId: dialog.id(),70 });71 } else {72 elements.delete(dialog.element());73 }74 }75 for (const element of elements) {76 const dialog = Dialog.createIfSupported(element);77 if (!dialog)78 continue;79 this._dialogs.set(dialog.id(), dialog);80 this._chromeSession.emitEvent('Page.dialogOpened', {81 pageId: this._pageId,82 dialogId: dialog.id(),83 type: dialog.type(),84 message: dialog.message(),85 defaultValue: dialog.defaultValue(),86 });87 }88 }89 onLocationChange(aWebProgress, aRequest, aLocation) {90 if (this._skipNextNavigation) {91 this._skipNextNavigation = false;92 return;93 }94 this._chromeSession.emitEvent('Browser.tabNavigated', {95 pageId: this._pageId,96 url: aLocation.spec97 });98 }99 url() {100 return this._browser.currentURI.spec;101 }102 tab() {103 return this._tab;104 }105 id() {106 return this._pageId;107 }108 async enable() {109 if (this._enabled)110 return;111 this._enabled = true;112 this._initializeDialogEvents();113 this._contentSession = new ContentSession(this._chromeSession, this._browser, this._pageId);114 await this._contentSession.send('enable');115 }116 async screenshot(options) {117 return await this._contentSession.send('screenshot', options);118 }119 async getBoundingBox(options) {120 return await this._contentSession.send('getBoundingBox', options);121 }122 async getContentQuads(options) {123 return await this._contentSession.send('getContentQuads', options);124 }125 /**126 * @param {{frameId: string, url: string}} options127 */128 async navigate(options) {129 return await this._contentSession.send('navigate', options);130 }131 /**132 * @param {{frameId: string, url: string}} options133 */134 async goBack(options) {135 return await this._contentSession.send('goBack', options);136 }137 /**138 * @param {{frameId: string, url: string}} options139 */140 async goForward(options) {141 return await this._contentSession.send('goForward', options);142 }143 /**144 * @param {{frameId: string, url: string}} options145 */146 async reload(options) {147 return await this._contentSession.send('reload', options);148 }149 /**150 * @param {{functionText: String, frameId: String}} options151 * @return {!Promise<*>}152 */153 async evaluate(options) {154 return await this._contentSession.send('evaluate', options);155 }156 async getObjectProperties(options) {157 return await this._contentSession.send('getObjectProperties', options);158 }159 async addScriptToEvaluateOnNewDocument(options) {160 return await this._contentSession.send('addScriptToEvaluateOnNewDocument', options);161 }162 async removeScriptToEvaluateOnNewDocument(options) {163 return await this._contentSession.send('removeScriptToEvaluateOnNewDocument', options);164 }165 async disposeObject(options) {166 return await this._contentSession.send('disposeObject', options);167 }168 async dispatchKeyEvent(options) {169 return await this._contentSession.send('dispatchKeyEvent', options);170 }171 async dispatchMouseEvent(options) {172 return await this._contentSession.send('dispatchMouseEvent', options);173 }174 async insertText(options) {175 return await this._contentSession.send('insertText', options);176 }177 async handleDialog({dialogId, accept, promptText}) {178 const dialog = this._dialogs.get(dialogId);179 if (!dialog)180 throw new Error('Failed to find dialog with id = ' + dialogId);181 if (accept)182 dialog.accept(promptText);183 else184 dialog.dismiss();185 }186 dispose() {187 this._browser.removeProgressListener(this);188 if (this._contentSession) {189 this._contentSession.dispose();190 this._contentSession = null;191 }192 }193}194class ContentSession {195 constructor(chromeSession, browser, pageId) {196 this._chromeSession = chromeSession;197 this._browser = browser;198 this._pageId = pageId;199 this._messageId = 0;200 this._pendingMessages = new Map();201 this._sessionId = helper.generateId();202 this._browser.messageManager.sendAsyncMessage('juggler:create-content-session', this._sessionId);203 this._eventListeners = [204 helper.addMessageListener(this._browser.messageManager, this._sessionId, {205 receiveMessage: message => this._onMessage(message)206 }),207 ];208 }209 dispose() {210 helper.removeListeners(this._eventListeners);211 for (const {resolve, reject} of this._pendingMessages.values())212 reject(new Error('Page closed.'));213 this._pendingMessages.clear();214 }215 /**216 * @param {string} methodName217 * @param {*} params218 * @return {!Promise<*>}219 */220 send(methodName, params) {221 const id = ++this._messageId;222 const promise = new Promise((resolve, reject) => {223 this._pendingMessages.set(id, {resolve, reject});224 });225 this._browser.messageManager.sendAsyncMessage(this._sessionId, {id, methodName, params});226 return promise;227 }228 _onMessage({data}) {229 if (data.id) {230 let id = data.id;231 const {resolve, reject} = this._pendingMessages.get(data.id);232 this._pendingMessages.delete(data.id);233 if (data.error)234 reject(new Error(data.error));235 else236 resolve(data.result);237 } else {238 const {239 eventName,240 params = {}241 } = data;242 params.pageId = this._pageId;243 this._chromeSession.emitEvent(eventName, params);244 }245 }246}247class Dialog {248 static createIfSupported(element) {249 const type = element.Dialog.args.promptType;250 switch (type) {251 case 'alert':252 case 'prompt':253 case 'confirm':254 return new Dialog(element, type);255 case 'confirmEx':256 return new Dialog(element, 'beforeunload');257 default:258 return null;259 };260 }261 constructor(element, type) {262 this._id = helper.generateId();263 this._type = type;264 this._element = element;265 }266 id() {267 return this._id;268 }269 message() {270 return this._element.ui.infoBody.textContent;271 }272 type() {273 return this._type;274 }275 element() {276 return this._element;277 }278 dismiss() {279 if (this._element.ui.button1)280 this._element.ui.button1.click();281 else282 this._element.ui.button0.click();283 }284 defaultValue() {285 return this._element.ui.loginTextbox.value;286 }287 accept(promptValue) {288 if (typeof promptValue === 'string' && this._type === 'prompt')289 this._element.ui.loginTextbox.value = promptValue;290 this._element.ui.button0.click();291 }292}293var EXPORTED_SYMBOLS = ['PageHandler'];...
main.js
Source:main.js
...95 },96 hasFailedToOverrideTimezone() {97 return failedToOverrideTimezone;98 },99 async awaitViewportDimensions({width, height, deviceSizeIsPageSize}) {100 docShell.deviceSizeIsPageSize = deviceSizeIsPageSize;101 const win = docShell.domWindow;102 if (win.innerWidth === width && win.innerHeight === height)103 return;104 await new Promise(resolve => {105 const listener = helper.addEventListener(win, 'resize', () => {106 if (win.innerWidth === width && win.innerHeight === height) {107 helper.removeListeners([listener]);108 resolve();109 }110 });111 });112 },113 dispose() {...
Using AI Code Generation
1const playwright = require('playwright');2(async () => {3 for (const browserType of ['chromium', 'firefox', 'webkit']) {4 const browser = await playwright[browserType].launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await page.waitForSelector('h1');8 const dimensions = await page._client.send('Emulation.getVisibleSize');9 console.log(dimensions);10 await browser.close();11 }12})();13{ width: 800, height: 600 }14{ width: 800, height: 600 }15{ width: 800, height: 600 }
Using AI Code Generation
1const playwright = require('playwright');2(async () => {3 const browser = await playwright['chromium'].launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.waitForLoadState('networkidle');7 const dimensions = await page.evaluate(() => {8 return {9 };10 });11 console.log('Dimensions:', dimensions);12 await browser.close();13})();14const playwright = require('playwright');15(async () => {16 const browser = await playwright['chromium'].launch();17 const context = await browser.newContext();18 const page = await context.newPage();19 await page.waitForLoadState('networkidle');20 await page.waitForViewportDimensions({21 });22 const dimensions = await page.evaluate(() => {23 return {24 };25 });26 console.log('Dimensions:', dimensions);27 await browser.close();28})();29const playwright = require('playwright');30(async () => {31 const browser = await playwright['chromium'].launch();32 const context = await browser.newContext();33 const page = await context.newPage();34 await page.waitForLoadState('networkidle');35 await page.waitForViewportDimensions({36 });37 const dimensions = await page.evaluate(() => {38 return {39 };40 });41 console.log('Dimensions:', dimensions);42 await browser.close();43})();44const playwright = require('playwright');45(async () => {46 const browser = await playwright['chromium'].launch();47 const context = await browser.newContext();
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const { width, height } = await page._delegate.awaitViewportDimensions();7 console.log(width, height);8 await browser.close();9})();
Using AI Code Generation
1const playwright = require('playwright');2(async () => {3 const browser = await playwright.chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const dimensions = await page._delegate._mainFrameSession._client.send('Emulation.getVisibleSize');7 console.log(dimensions);8 await browser.close();9})();10Output: { width: 800, height: 600 }
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const page = await browser.newPage();5 await page.setViewportSize({ width: 800, height: 600 });6 await page.screenshot({ path: 'example.png' });7 await browser.close();8})();9import { PlaywrightTestConfig } from '@playwright/test';10const config: PlaywrightTestConfig = {11 use: {12 viewport: { width: 800, height: 600 },13 },14};15export default config;16const { chromium } = require('playwright');17(async () => {18 const browser = await chromium.launch();19 const page = await browser.newPage();20 await page.setViewportSize({ width: 800, height: 600 });21 await page.screenshot({ path: 'example.png' });22 await browser.close();23})();24import { PlaywrightTestConfig } from '@playwright/test';25const config: PlaywrightTestConfig = {26 use: {27 viewport: { width: 800, height: 600 },28 },29};30export default config;31import { PlaywrightTestConfig } from '@playwright/test';32const config: PlaywrightTestConfig = {33 use: {34 viewport: { width: 800, height: 600 },
Using AI Code Generation
1const { awaitViewportDimensions } = require('playwright/lib/server/browserContext');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const [width, height] = await awaitViewportDimensions.call(page);8 console.log(width, height);9 await browser.close();10})();
Using AI Code Generation
1const { test, expect } = require('@playwright/test');2test('test', async ({ page }) => {3 await page.setViewportSize({ width: 1280, height: 720 });4 await page.waitForLoadState();5 const dimensions = await page._delegate._mainFrameSession._client.send('Emulation.getVisibleSize');6 expect(dimensions).toEqual({ width: 1280, height: 720 });7});8const { test, expect } = require('@playwright/test');9test('test', async ({ page }) => {10 await page.setViewportSize({ width: 1280, height: 720 });11 await page.waitForLoadState();12 const dimensions = await page.viewportSize();13 expect(dimensions).toEqual({ width: 1280, height: 720 });14});
Using AI Code Generation
1const playwright = require("playwright");2const { getViewportDimensions } = require("playwright/lib/server/supplements/recorder/recorderSupplement.js");3(async () => {4 const browser = await playwright["chromium"].launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const dimensions = await getViewportDimensions(page);8 console.log(dimensions);9 await browser.close();10})();11{ width: 1280, height: 800 }12const playwright = require("playwright");13const { getViewportDimensions } = require("playwright/lib/server/supplements/recorder/recorderSupplement.js");14(async () => {15 const browser = await playwright["chromium"].launch();16 const context = await browser.newContext();17 const page = await context.newPage();18 const dimensions = await getViewportDimensions(page);19 console.log(dimensions);20 await browser.close();21})();22const playwright = require("playwright");23const { getViewportDimensions } = require("playwright/lib/server/supplements/recorder/recorderSupplement.js");24(async () => {25 const browser = await playwright["chromium"].launch();26 const context = await browser.newContext();27 const page = await context.newPage();28 const dimensions = await getViewportDimensions(page);29 console.log(dimensions);30 await page.close();31 await browser.close();32})();33const playwright = require("playwright");34const { getViewportDimensions } = require("playwright/lib/server/supplements/recorder/recorderSupplement.js");35(async () => {36 const browser = await playwright["chromium"].launch();37 const context = await browser.newContext();38 const page = await context.newPage();39 await page.setViewportSize({ width: 1600, height: 900 });40 const dimensions = await getViewportDimensions(page);41 console.log(dimensions);
Using AI Code Generation
1const playwright = require('playwright');2const { chromium } = playwright;3const { devices } = require('playwright');4const iPhone = devices['iPhone 6'];5(async () => {6 const browser = await chromium.launch();7 const context = await browser.newContext({8 viewport: { width: 800, height: 600 },9 });10 const page = await context.newPage();11 await page.waitForTimeout(3000);12 const dimensions = await page.mainFrame().awaitViewportDimensions();13 console.log(dimensions);14 await browser.close();15})();
Using AI Code Generation
1import { Page } from 'playwright';2import { InternalAPI } from 'playwright-core/lib/internal';3export async function getViewportDimensions(page: Page) {4 const internalAPI = new InternalAPI(page);5 const { width, height } = await internalAPI.awaitViewportDimensions();6 return { width, height };7}8import { getViewportDimensions } from './test.js';9const { width, height } = await getViewportDimensions(page);10import { Page } from 'playwright';11import { InternalAPI } from 'playwright-core/lib/internal';12export async function getViewportDimensions(page: Page) {13 const internalAPI = new InternalAPI(page);14 const { width, height } = await internalAPI.awaitViewportDimensions();15 return { width, height };16}17import { getViewportDimensions } from './test.js';18const { width, height } = await getViewportDimensions(page);19import { Page } from 'playwright';20import { InternalAPI } from 'playwright-core/lib/internal';21export async function getViewportDimensions(page: Page) {22 const internalAPI = new InternalAPI(page);23 const { width, height } = await internalAPI.awaitViewportDimensions();24 return { width, height };25}26import { getViewportDimensions } from './test.js';27const { width, height } = await getViewportDimensions(page);28import { Page } from 'playwright';29import { InternalAPI } from 'playwright-core/lib/internal';30export async function getViewportDimensions(page: Page) {31 const internalAPI = new InternalAPI(page);32 const { width, height } = await internalAPI.awaitViewportDimensions();33 return { width, height };34}35import { getViewportDimensions } from './test.js';36const { width, height } = await getViewportDimensions(page);
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!