frames.js 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471
  1. "use strict";
  2. var __create = Object.create;
  3. var __defProp = Object.defineProperty;
  4. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  5. var __getOwnPropNames = Object.getOwnPropertyNames;
  6. var __getProtoOf = Object.getPrototypeOf;
  7. var __hasOwnProp = Object.prototype.hasOwnProperty;
  8. var __export = (target, all) => {
  9. for (var name in all)
  10. __defProp(target, name, { get: all[name], enumerable: true });
  11. };
  12. var __copyProps = (to, from, except, desc) => {
  13. if (from && typeof from === "object" || typeof from === "function") {
  14. for (let key of __getOwnPropNames(from))
  15. if (!__hasOwnProp.call(to, key) && key !== except)
  16. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  17. }
  18. return to;
  19. };
  20. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  21. // If the importer is in node compatibility mode or this is not an ESM
  22. // file that has been converted to a CommonJS file using a Babel-
  23. // compatible transform (i.e. "__esModule" has not been set), then set
  24. // "default" to the CommonJS "module.exports" for node compatibility.
  25. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  26. mod
  27. ));
  28. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  29. var frames_exports = {};
  30. __export(frames_exports, {
  31. Frame: () => Frame,
  32. FrameManager: () => FrameManager,
  33. NavigationAbortedError: () => NavigationAbortedError
  34. });
  35. module.exports = __toCommonJS(frames_exports);
  36. var import_browserContext = require("./browserContext");
  37. var dom = __toESM(require("./dom"));
  38. var import_errors = require("./errors");
  39. var import_fileUploadUtils = require("./fileUploadUtils");
  40. var import_frameSelectors = require("./frameSelectors");
  41. var import_helper = require("./helper");
  42. var import_instrumentation = require("./instrumentation");
  43. var js = __toESM(require("./javascript"));
  44. var network = __toESM(require("./network"));
  45. var import_page = require("./page");
  46. var import_progress = require("./progress");
  47. var types = __toESM(require("./types"));
  48. var import_utils = require("../utils");
  49. var import_protocolError = require("./protocolError");
  50. var import_debugLogger = require("./utils/debugLogger");
  51. var import_eventsHelper = require("./utils/eventsHelper");
  52. var import_selectorParser = require("../utils/isomorphic/selectorParser");
  53. var import_manualPromise = require("../utils/isomorphic/manualPromise");
  54. var import_callLog = require("./callLog");
  55. class NavigationAbortedError extends Error {
  56. constructor(documentId, message) {
  57. super(message);
  58. this.documentId = documentId;
  59. }
  60. }
  61. const kDummyFrameId = "<dummy>";
  62. class FrameManager {
  63. constructor(page) {
  64. this._frames = /* @__PURE__ */ new Map();
  65. this._consoleMessageTags = /* @__PURE__ */ new Map();
  66. this._signalBarriers = /* @__PURE__ */ new Set();
  67. this._webSockets = /* @__PURE__ */ new Map();
  68. this._nextFrameSeq = 0;
  69. this._page = page;
  70. this._mainFrame = void 0;
  71. }
  72. nextFrameSeq() {
  73. return this._nextFrameSeq++;
  74. }
  75. createDummyMainFrameIfNeeded() {
  76. if (!this._mainFrame)
  77. this.frameAttached(kDummyFrameId, null);
  78. }
  79. dispose() {
  80. for (const frame of this._frames.values()) {
  81. frame._stopNetworkIdleTimer();
  82. frame._invalidateNonStallingEvaluations("Target crashed");
  83. }
  84. }
  85. mainFrame() {
  86. return this._mainFrame;
  87. }
  88. frames() {
  89. const frames = [];
  90. collect(this._mainFrame);
  91. return frames;
  92. function collect(frame) {
  93. frames.push(frame);
  94. for (const subframe of frame.childFrames())
  95. collect(subframe);
  96. }
  97. }
  98. frame(frameId) {
  99. return this._frames.get(frameId) || null;
  100. }
  101. frameAttached(frameId, parentFrameId) {
  102. const parentFrame = parentFrameId ? this._frames.get(parentFrameId) : null;
  103. if (!parentFrame) {
  104. if (this._mainFrame) {
  105. this._frames.delete(this._mainFrame._id);
  106. this._mainFrame._id = frameId;
  107. } else {
  108. (0, import_utils.assert)(!this._frames.has(frameId));
  109. this._mainFrame = new Frame(this._page, frameId, parentFrame);
  110. }
  111. this._frames.set(frameId, this._mainFrame);
  112. return this._mainFrame;
  113. } else {
  114. (0, import_utils.assert)(!this._frames.has(frameId));
  115. const frame = new Frame(this._page, frameId, parentFrame);
  116. this._frames.set(frameId, frame);
  117. this._page.emit(import_page.Page.Events.FrameAttached, frame);
  118. return frame;
  119. }
  120. }
  121. async waitForSignalsCreatedBy(progress, waitAfter, action) {
  122. if (!waitAfter)
  123. return action();
  124. const barrier = new SignalBarrier(progress);
  125. this._signalBarriers.add(barrier);
  126. try {
  127. const result = await action();
  128. await progress.race(this._page.delegate.inputActionEpilogue());
  129. await barrier.waitFor();
  130. await new Promise((0, import_utils.makeWaitForNextTask)());
  131. return result;
  132. } finally {
  133. this._signalBarriers.delete(barrier);
  134. }
  135. }
  136. frameWillPotentiallyRequestNavigation() {
  137. for (const barrier of this._signalBarriers)
  138. barrier.retain();
  139. }
  140. frameDidPotentiallyRequestNavigation() {
  141. for (const barrier of this._signalBarriers)
  142. barrier.release();
  143. }
  144. frameRequestedNavigation(frameId, documentId) {
  145. const frame = this._frames.get(frameId);
  146. if (!frame)
  147. return;
  148. for (const barrier of this._signalBarriers)
  149. barrier.addFrameNavigation(frame);
  150. if (frame.pendingDocument() && frame.pendingDocument().documentId === documentId) {
  151. return;
  152. }
  153. const request = documentId ? Array.from(frame._inflightRequests).find((request2) => request2._documentId === documentId) : void 0;
  154. frame.setPendingDocument({ documentId, request });
  155. }
  156. frameCommittedNewDocumentNavigation(frameId, url, name, documentId, initial) {
  157. const frame = this._frames.get(frameId);
  158. this.removeChildFramesRecursively(frame);
  159. this.clearWebSockets(frame);
  160. frame._url = url;
  161. frame._name = name;
  162. let keepPending;
  163. const pendingDocument = frame.pendingDocument();
  164. if (pendingDocument) {
  165. if (pendingDocument.documentId === void 0) {
  166. pendingDocument.documentId = documentId;
  167. }
  168. if (pendingDocument.documentId === documentId) {
  169. frame._currentDocument = pendingDocument;
  170. } else {
  171. keepPending = pendingDocument;
  172. frame._currentDocument = { documentId, request: void 0 };
  173. }
  174. frame.setPendingDocument(void 0);
  175. } else {
  176. frame._currentDocument = { documentId, request: void 0 };
  177. }
  178. frame._onClearLifecycle();
  179. const navigationEvent = { url, name, newDocument: frame._currentDocument, isPublic: true };
  180. this._fireInternalFrameNavigation(frame, navigationEvent);
  181. if (!initial) {
  182. import_debugLogger.debugLogger.log("api", ` navigated to "${url}"`);
  183. this._page.frameNavigatedToNewDocument(frame);
  184. }
  185. frame.setPendingDocument(keepPending);
  186. }
  187. frameCommittedSameDocumentNavigation(frameId, url) {
  188. const frame = this._frames.get(frameId);
  189. if (!frame)
  190. return;
  191. const pending = frame.pendingDocument();
  192. if (pending && pending.documentId === void 0 && pending.request === void 0) {
  193. frame.setPendingDocument(void 0);
  194. }
  195. frame._url = url;
  196. const navigationEvent = { url, name: frame._name, isPublic: true };
  197. this._fireInternalFrameNavigation(frame, navigationEvent);
  198. import_debugLogger.debugLogger.log("api", ` navigated to "${url}"`);
  199. }
  200. frameAbortedNavigation(frameId, errorText, documentId) {
  201. const frame = this._frames.get(frameId);
  202. if (!frame || !frame.pendingDocument())
  203. return;
  204. if (documentId !== void 0 && frame.pendingDocument().documentId !== documentId)
  205. return;
  206. const navigationEvent = {
  207. url: frame._url,
  208. name: frame._name,
  209. newDocument: frame.pendingDocument(),
  210. error: new NavigationAbortedError(documentId, errorText),
  211. isPublic: !(documentId && frame._redirectedNavigations.has(documentId))
  212. };
  213. frame.setPendingDocument(void 0);
  214. this._fireInternalFrameNavigation(frame, navigationEvent);
  215. }
  216. frameDetached(frameId) {
  217. const frame = this._frames.get(frameId);
  218. if (frame) {
  219. this._removeFramesRecursively(frame);
  220. this._page.mainFrame()._recalculateNetworkIdle();
  221. }
  222. }
  223. frameLifecycleEvent(frameId, event) {
  224. const frame = this._frames.get(frameId);
  225. if (frame)
  226. frame._onLifecycleEvent(event);
  227. }
  228. requestStarted(request, route) {
  229. const frame = request.frame();
  230. this._inflightRequestStarted(request);
  231. if (request._documentId)
  232. frame.setPendingDocument({ documentId: request._documentId, request });
  233. if (request._isFavicon) {
  234. route?.abort("aborted").catch(() => {
  235. });
  236. return;
  237. }
  238. this._page.addNetworkRequest(request);
  239. this._page.emitOnContext(import_browserContext.BrowserContext.Events.Request, request);
  240. if (route)
  241. new network.Route(request, route).handle([...this._page.requestInterceptors, ...this._page.browserContext.requestInterceptors]);
  242. }
  243. requestReceivedResponse(response) {
  244. if (response.request()._isFavicon)
  245. return;
  246. this._page.emitOnContext(import_browserContext.BrowserContext.Events.Response, response);
  247. }
  248. reportRequestFinished(request, response) {
  249. this._inflightRequestFinished(request);
  250. if (request._isFavicon)
  251. return;
  252. this._page.emitOnContext(import_browserContext.BrowserContext.Events.RequestFinished, { request, response });
  253. }
  254. requestFailed(request, canceled) {
  255. const frame = request.frame();
  256. this._inflightRequestFinished(request);
  257. if (frame.pendingDocument() && frame.pendingDocument().request === request) {
  258. let errorText = request.failure().errorText;
  259. if (canceled)
  260. errorText += "; maybe frame was detached?";
  261. this.frameAbortedNavigation(frame._id, errorText, frame.pendingDocument().documentId);
  262. }
  263. if (request._isFavicon)
  264. return;
  265. this._page.emitOnContext(import_browserContext.BrowserContext.Events.RequestFailed, request);
  266. }
  267. removeChildFramesRecursively(frame) {
  268. for (const child of frame.childFrames())
  269. this._removeFramesRecursively(child);
  270. }
  271. _removeFramesRecursively(frame) {
  272. this.removeChildFramesRecursively(frame);
  273. frame._onDetached();
  274. this._frames.delete(frame._id);
  275. if (!this._page.isClosed())
  276. this._page.emit(import_page.Page.Events.FrameDetached, frame);
  277. }
  278. _inflightRequestFinished(request) {
  279. const frame = request.frame();
  280. if (request._isFavicon)
  281. return;
  282. if (!frame._inflightRequests.has(request))
  283. return;
  284. frame._inflightRequests.delete(request);
  285. if (frame._inflightRequests.size === 0)
  286. frame._startNetworkIdleTimer();
  287. }
  288. _inflightRequestStarted(request) {
  289. const frame = request.frame();
  290. if (request._isFavicon)
  291. return;
  292. frame._inflightRequests.add(request);
  293. if (frame._inflightRequests.size === 1)
  294. frame._stopNetworkIdleTimer();
  295. }
  296. interceptConsoleMessage(message) {
  297. if (message.type() !== "debug")
  298. return false;
  299. const tag = message.text();
  300. const handler = this._consoleMessageTags.get(tag);
  301. if (!handler)
  302. return false;
  303. this._consoleMessageTags.delete(tag);
  304. handler();
  305. return true;
  306. }
  307. clearWebSockets(frame) {
  308. if (frame.parentFrame())
  309. return;
  310. this._webSockets.clear();
  311. }
  312. onWebSocketCreated(requestId, url) {
  313. const ws = new network.WebSocket(this._page, url);
  314. this._webSockets.set(requestId, ws);
  315. }
  316. onWebSocketRequest(requestId) {
  317. const ws = this._webSockets.get(requestId);
  318. if (ws && ws.markAsNotified())
  319. this._page.emit(import_page.Page.Events.WebSocket, ws);
  320. }
  321. onWebSocketResponse(requestId, status, statusText) {
  322. const ws = this._webSockets.get(requestId);
  323. if (status < 400)
  324. return;
  325. if (ws)
  326. ws.error(`${statusText}: ${status}`);
  327. }
  328. onWebSocketFrameSent(requestId, opcode, data) {
  329. const ws = this._webSockets.get(requestId);
  330. if (ws)
  331. ws.frameSent(opcode, data);
  332. }
  333. webSocketFrameReceived(requestId, opcode, data) {
  334. const ws = this._webSockets.get(requestId);
  335. if (ws)
  336. ws.frameReceived(opcode, data);
  337. }
  338. webSocketClosed(requestId) {
  339. const ws = this._webSockets.get(requestId);
  340. if (ws)
  341. ws.closed();
  342. this._webSockets.delete(requestId);
  343. }
  344. webSocketError(requestId, errorMessage) {
  345. const ws = this._webSockets.get(requestId);
  346. if (ws)
  347. ws.error(errorMessage);
  348. }
  349. _fireInternalFrameNavigation(frame, event) {
  350. frame.emit(Frame.Events.InternalNavigation, event);
  351. }
  352. }
  353. const FrameEvent = {
  354. InternalNavigation: "internalnavigation",
  355. AddLifecycle: "addlifecycle",
  356. RemoveLifecycle: "removelifecycle"
  357. };
  358. class Frame extends import_instrumentation.SdkObject {
  359. constructor(page, id, parentFrame) {
  360. super(page, "frame");
  361. this._firedLifecycleEvents = /* @__PURE__ */ new Set();
  362. this._firedNetworkIdleSelf = false;
  363. this._url = "";
  364. this._contextData = /* @__PURE__ */ new Map();
  365. this._childFrames = /* @__PURE__ */ new Set();
  366. this._name = "";
  367. this._inflightRequests = /* @__PURE__ */ new Set();
  368. this._setContentCounter = 0;
  369. this._detachedScope = new import_utils.LongStandingScope();
  370. this._raceAgainstEvaluationStallingEventsPromises = /* @__PURE__ */ new Set();
  371. this._redirectedNavigations = /* @__PURE__ */ new Map();
  372. this.attribution.frame = this;
  373. this.seq = page.frameManager.nextFrameSeq();
  374. this._id = id;
  375. this._page = page;
  376. this._parentFrame = parentFrame;
  377. this._currentDocument = { documentId: void 0, request: void 0 };
  378. this.selectors = new import_frameSelectors.FrameSelectors(this);
  379. this._contextData.set("main", { contextPromise: new import_manualPromise.ManualPromise(), context: null });
  380. this._contextData.set("utility", { contextPromise: new import_manualPromise.ManualPromise(), context: null });
  381. this._setContext("main", null);
  382. this._setContext("utility", null);
  383. if (this._parentFrame)
  384. this._parentFrame._childFrames.add(this);
  385. this._firedLifecycleEvents.add("commit");
  386. if (id !== kDummyFrameId)
  387. this._startNetworkIdleTimer();
  388. }
  389. static {
  390. this.Events = FrameEvent;
  391. }
  392. isDetached() {
  393. return this._detachedScope.isClosed();
  394. }
  395. _onLifecycleEvent(event) {
  396. if (this._firedLifecycleEvents.has(event))
  397. return;
  398. this._firedLifecycleEvents.add(event);
  399. this.emit(Frame.Events.AddLifecycle, event);
  400. if (this === this._page.mainFrame() && this._url !== "about:blank")
  401. import_debugLogger.debugLogger.log("api", ` "${event}" event fired`);
  402. this._page.mainFrame()._recalculateNetworkIdle();
  403. }
  404. _onClearLifecycle() {
  405. for (const event of this._firedLifecycleEvents)
  406. this.emit(Frame.Events.RemoveLifecycle, event);
  407. this._firedLifecycleEvents.clear();
  408. this._inflightRequests = new Set(Array.from(this._inflightRequests).filter((request) => request === this._currentDocument.request));
  409. this._stopNetworkIdleTimer();
  410. if (this._inflightRequests.size === 0)
  411. this._startNetworkIdleTimer();
  412. this._page.mainFrame()._recalculateNetworkIdle(this);
  413. this._onLifecycleEvent("commit");
  414. }
  415. setPendingDocument(documentInfo) {
  416. this._pendingDocument = documentInfo;
  417. if (documentInfo)
  418. this._invalidateNonStallingEvaluations("Navigation interrupted the evaluation");
  419. }
  420. pendingDocument() {
  421. return this._pendingDocument;
  422. }
  423. _invalidateNonStallingEvaluations(message) {
  424. if (!this._raceAgainstEvaluationStallingEventsPromises.size)
  425. return;
  426. const error = new Error(message);
  427. for (const promise of this._raceAgainstEvaluationStallingEventsPromises)
  428. promise.reject(error);
  429. }
  430. async raceAgainstEvaluationStallingEvents(cb) {
  431. if (this._pendingDocument)
  432. throw new Error("Frame is currently attempting a navigation");
  433. if (this._page.browserContext.dialogManager.hasOpenDialogsForPage(this._page))
  434. throw new Error("Open JavaScript dialog prevents evaluation");
  435. const promise = new import_manualPromise.ManualPromise();
  436. this._raceAgainstEvaluationStallingEventsPromises.add(promise);
  437. try {
  438. return await Promise.race([
  439. cb(),
  440. promise
  441. ]);
  442. } finally {
  443. this._raceAgainstEvaluationStallingEventsPromises.delete(promise);
  444. }
  445. }
  446. nonStallingRawEvaluateInExistingMainContext(expression) {
  447. return this.raceAgainstEvaluationStallingEvents(() => {
  448. const context = this._existingMainContext();
  449. if (!context)
  450. throw new Error("Frame does not yet have a main execution context");
  451. return context.rawEvaluateJSON(expression);
  452. });
  453. }
  454. nonStallingEvaluateInExistingContext(expression, world) {
  455. return this.raceAgainstEvaluationStallingEvents(() => {
  456. const context = this._contextData.get(world)?.context;
  457. if (!context)
  458. throw new Error("Frame does not yet have the execution context");
  459. return context.evaluateExpression(expression, { isFunction: false });
  460. });
  461. }
  462. _recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle) {
  463. let isNetworkIdle = this._firedNetworkIdleSelf;
  464. for (const child of this._childFrames) {
  465. child._recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle);
  466. if (!child._firedLifecycleEvents.has("networkidle"))
  467. isNetworkIdle = false;
  468. }
  469. if (isNetworkIdle && !this._firedLifecycleEvents.has("networkidle")) {
  470. this._firedLifecycleEvents.add("networkidle");
  471. this.emit(Frame.Events.AddLifecycle, "networkidle");
  472. if (this === this._page.mainFrame() && this._url !== "about:blank")
  473. import_debugLogger.debugLogger.log("api", ` "networkidle" event fired`);
  474. }
  475. if (frameThatAllowsRemovingNetworkIdle !== this && this._firedLifecycleEvents.has("networkidle") && !isNetworkIdle) {
  476. this._firedLifecycleEvents.delete("networkidle");
  477. this.emit(Frame.Events.RemoveLifecycle, "networkidle");
  478. }
  479. }
  480. async raceNavigationAction(progress, action) {
  481. return import_utils.LongStandingScope.raceMultiple([
  482. this._detachedScope,
  483. this._page.openScope
  484. ], action().catch((e) => {
  485. if (e instanceof NavigationAbortedError && e.documentId) {
  486. const data = this._redirectedNavigations.get(e.documentId);
  487. if (data) {
  488. progress.log(`waiting for redirected navigation to "${data.url}"`);
  489. return progress.race(data.gotoPromise);
  490. }
  491. }
  492. throw e;
  493. }));
  494. }
  495. redirectNavigation(url, documentId, referer) {
  496. const controller = new import_progress.ProgressController();
  497. const data = {
  498. url,
  499. gotoPromise: controller.run((progress) => this.gotoImpl(progress, url, { referer }), 0)
  500. };
  501. this._redirectedNavigations.set(documentId, data);
  502. data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId));
  503. }
  504. async goto(progress, url, options = {}) {
  505. const constructedNavigationURL = (0, import_utils.constructURLBasedOnBaseURL)(this._page.browserContext._options.baseURL, url);
  506. return this.raceNavigationAction(progress, async () => this.gotoImpl(progress, constructedNavigationURL, options));
  507. }
  508. async gotoImpl(progress, url, options) {
  509. const waitUntil = verifyLifecycle("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
  510. progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
  511. const headers = this._page.extraHTTPHeaders() || [];
  512. const refererHeader = headers.find((h) => h.name.toLowerCase() === "referer");
  513. let referer = refererHeader ? refererHeader.value : void 0;
  514. if (options.referer !== void 0) {
  515. if (referer !== void 0 && referer !== options.referer)
  516. throw new Error('"referer" is already specified as extra HTTP header');
  517. referer = options.referer;
  518. }
  519. url = import_helper.helper.completeUserURL(url);
  520. const navigationEvents = [];
  521. const collectNavigations = (arg) => navigationEvents.push(arg);
  522. this.on(Frame.Events.InternalNavigation, collectNavigations);
  523. const navigateResult = await progress.race(this._page.delegate.navigateFrame(this, url, referer)).finally(
  524. () => this.off(Frame.Events.InternalNavigation, collectNavigations)
  525. );
  526. let event;
  527. if (navigateResult.newDocumentId) {
  528. const predicate = (event2) => {
  529. return event2.newDocument && (event2.newDocument.documentId === navigateResult.newDocumentId || !event2.error);
  530. };
  531. const events = navigationEvents.filter(predicate);
  532. if (events.length)
  533. event = events[0];
  534. else
  535. event = await import_helper.helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, predicate).promise;
  536. if (event.newDocument.documentId !== navigateResult.newDocumentId) {
  537. throw new NavigationAbortedError(navigateResult.newDocumentId, `Navigation to "${url}" is interrupted by another navigation to "${event.url}"`);
  538. }
  539. if (event.error)
  540. throw event.error;
  541. } else {
  542. const predicate = (e) => !e.newDocument;
  543. const events = navigationEvents.filter(predicate);
  544. if (events.length)
  545. event = events[0];
  546. else
  547. event = await import_helper.helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, predicate).promise;
  548. }
  549. if (!this._firedLifecycleEvents.has(waitUntil))
  550. await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
  551. const request = event.newDocument ? event.newDocument.request : void 0;
  552. const response = request ? progress.race(request._finalRequest().response()) : null;
  553. return response;
  554. }
  555. async _waitForNavigation(progress, requiresNewDocument, options) {
  556. const waitUntil = verifyLifecycle("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
  557. progress.log(`waiting for navigation until "${waitUntil}"`);
  558. const navigationEvent = await import_helper.helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (event) => {
  559. if (event.error)
  560. return true;
  561. if (requiresNewDocument && !event.newDocument)
  562. return false;
  563. progress.log(` navigated to "${this._url}"`);
  564. return true;
  565. }).promise;
  566. if (navigationEvent.error)
  567. throw navigationEvent.error;
  568. if (!this._firedLifecycleEvents.has(waitUntil))
  569. await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
  570. const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : void 0;
  571. return request ? progress.race(request._finalRequest().response()) : null;
  572. }
  573. async waitForLoadState(progress, state) {
  574. const waitUntil = verifyLifecycle("state", state);
  575. if (!this._firedLifecycleEvents.has(waitUntil))
  576. await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
  577. }
  578. async frameElement() {
  579. return this._page.delegate.getFrameElement(this);
  580. }
  581. _context(world) {
  582. return this._contextData.get(world).contextPromise.then((contextOrDestroyedReason) => {
  583. if (contextOrDestroyedReason instanceof js.ExecutionContext)
  584. return contextOrDestroyedReason;
  585. throw new Error(contextOrDestroyedReason.destroyedReason);
  586. });
  587. }
  588. _mainContext() {
  589. return this._context("main");
  590. }
  591. _existingMainContext() {
  592. return this._contextData.get("main")?.context || null;
  593. }
  594. _utilityContext() {
  595. return this._context("utility");
  596. }
  597. async evaluateExpression(expression, options = {}, arg) {
  598. const context = await this._context(options.world ?? "main");
  599. const value = await context.evaluateExpression(expression, options, arg);
  600. return value;
  601. }
  602. async evaluateExpressionHandle(expression, options = {}, arg) {
  603. const context = await this._context(options.world ?? "main");
  604. const value = await context.evaluateExpressionHandle(expression, options, arg);
  605. return value;
  606. }
  607. async querySelector(selector, options) {
  608. import_debugLogger.debugLogger.log("api", ` finding element using the selector "${selector}"`);
  609. return this.selectors.query(selector, options);
  610. }
  611. async waitForSelector(progress, selector, performActionPreChecksAndLog, options, scope) {
  612. if (options.visibility)
  613. throw new Error("options.visibility is not supported, did you mean options.state?");
  614. if (options.waitFor && options.waitFor !== "visible")
  615. throw new Error("options.waitFor is not supported, did you mean options.state?");
  616. const { state = "visible" } = options;
  617. if (!["attached", "detached", "visible", "hidden"].includes(state))
  618. throw new Error(`state: expected one of (attached|detached|visible|hidden)`);
  619. if (performActionPreChecksAndLog)
  620. progress.log(`waiting for ${this._asLocator(selector)}${state === "attached" ? "" : " to be " + state}`);
  621. const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async (continuePolling) => {
  622. if (performActionPreChecksAndLog)
  623. await this._page.performActionPreChecks(progress);
  624. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, options, scope));
  625. if (!resolved) {
  626. if (state === "hidden" || state === "detached")
  627. return null;
  628. return continuePolling;
  629. }
  630. const result = await progress.race(resolved.injected.evaluateHandle((injected, { info, root }) => {
  631. if (root && !root.isConnected)
  632. throw injected.createStacklessError("Element is not attached to the DOM");
  633. const elements = injected.querySelectorAll(info.parsed, root || document);
  634. const element2 = elements[0];
  635. const visible2 = element2 ? injected.utils.isElementVisible(element2) : false;
  636. let log2 = "";
  637. if (elements.length > 1) {
  638. if (info.strict)
  639. throw injected.strictModeViolationError(info.parsed, elements);
  640. log2 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`;
  641. } else if (element2) {
  642. log2 = ` locator resolved to ${visible2 ? "visible" : "hidden"} ${injected.previewNode(element2)}`;
  643. }
  644. injected.checkDeprecatedSelectorUsage(info.parsed, elements);
  645. return { log: log2, element: element2, visible: visible2, attached: !!element2 };
  646. }, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
  647. const { log, visible, attached } = await progress.race(result.evaluate((r) => ({ log: r.log, visible: r.visible, attached: r.attached })));
  648. if (log)
  649. progress.log(log);
  650. const success = { attached, detached: !attached, visible, hidden: !visible }[state];
  651. if (!success) {
  652. result.dispose();
  653. return continuePolling;
  654. }
  655. if (options.omitReturnValue) {
  656. result.dispose();
  657. return null;
  658. }
  659. const element = state === "attached" || state === "visible" ? await progress.race(result.evaluateHandle((r) => r.element)) : null;
  660. result.dispose();
  661. if (!element)
  662. return null;
  663. if (options.__testHookBeforeAdoptNode)
  664. await progress.race(options.__testHookBeforeAdoptNode());
  665. try {
  666. const mainContext = await progress.race(resolved.frame._mainContext());
  667. return await progress.race(element._adoptTo(mainContext));
  668. } catch (e) {
  669. return continuePolling;
  670. }
  671. });
  672. return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise;
  673. }
  674. async dispatchEvent(progress, selector, type, eventInit = {}, options, scope) {
  675. await this._callOnElementOnceMatches(progress, selector, (injectedScript, element, data) => {
  676. injectedScript.dispatchEvent(element, data.type, data.eventInit);
  677. }, { type, eventInit }, { mainWorld: true, ...options }, scope);
  678. }
  679. async evalOnSelector(selector, strict, expression, isFunction, arg, scope) {
  680. const handle = await this.selectors.query(selector, { strict }, scope);
  681. if (!handle)
  682. throw new Error(`Failed to find element matching selector "${selector}"`);
  683. const result = await handle.evaluateExpression(expression, { isFunction }, arg);
  684. handle.dispose();
  685. return result;
  686. }
  687. async evalOnSelectorAll(selector, expression, isFunction, arg, scope) {
  688. const arrayHandle = await this.selectors.queryArrayInMainWorld(selector, scope);
  689. const result = await arrayHandle.evaluateExpression(expression, { isFunction }, arg);
  690. arrayHandle.dispose();
  691. return result;
  692. }
  693. async maskSelectors(selectors, color) {
  694. const context = await this._utilityContext();
  695. const injectedScript = await context.injectedScript();
  696. await injectedScript.evaluate((injected, { parsed, color: color2 }) => {
  697. injected.maskSelectors(parsed, color2);
  698. }, { parsed: selectors, color });
  699. }
  700. async querySelectorAll(selector) {
  701. return this.selectors.queryAll(selector);
  702. }
  703. async queryCount(selector, options) {
  704. try {
  705. return await this.selectors.queryCount(selector, options);
  706. } catch (e) {
  707. if (this.isNonRetriableError(e))
  708. throw e;
  709. return 0;
  710. }
  711. }
  712. async content() {
  713. try {
  714. const context = await this._utilityContext();
  715. return await context.evaluate(() => {
  716. let retVal = "";
  717. if (document.doctype)
  718. retVal = new XMLSerializer().serializeToString(document.doctype);
  719. if (document.documentElement)
  720. retVal += document.documentElement.outerHTML;
  721. return retVal;
  722. });
  723. } catch (e) {
  724. if (this.isNonRetriableError(e))
  725. throw e;
  726. throw new Error(`Unable to retrieve content because the page is navigating and changing the content.`);
  727. }
  728. }
  729. async setContent(progress, html, options) {
  730. const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
  731. await this.raceNavigationAction(progress, async () => {
  732. const waitUntil = options.waitUntil === void 0 ? "load" : options.waitUntil;
  733. progress.log(`setting frame content, waiting until "${waitUntil}"`);
  734. const context = await progress.race(this._utilityContext());
  735. const tagPromise = new import_manualPromise.ManualPromise();
  736. this._page.frameManager._consoleMessageTags.set(tag, () => {
  737. this._onClearLifecycle();
  738. tagPromise.resolve();
  739. });
  740. const lifecyclePromise = progress.race(tagPromise).then(() => this.waitForLoadState(progress, waitUntil));
  741. const contentPromise = progress.race(context.evaluate(({ html: html2, tag: tag2 }) => {
  742. document.open();
  743. console.debug(tag2);
  744. document.write(html2);
  745. document.close();
  746. }, { html, tag }));
  747. await Promise.all([contentPromise, lifecyclePromise]);
  748. return null;
  749. }).finally(() => {
  750. this._page.frameManager._consoleMessageTags.delete(tag);
  751. });
  752. }
  753. name() {
  754. return this._name || "";
  755. }
  756. url() {
  757. return this._url;
  758. }
  759. origin() {
  760. if (!this._url.startsWith("http"))
  761. return;
  762. return network.parseURL(this._url)?.origin;
  763. }
  764. parentFrame() {
  765. return this._parentFrame;
  766. }
  767. childFrames() {
  768. return Array.from(this._childFrames);
  769. }
  770. async addScriptTag(params) {
  771. const {
  772. url = null,
  773. content = null,
  774. type = ""
  775. } = params;
  776. if (!url && !content)
  777. throw new Error("Provide an object with a `url`, `path` or `content` property");
  778. const context = await this._mainContext();
  779. return this._raceWithCSPError(async () => {
  780. if (url !== null)
  781. return (await context.evaluateHandle(addScriptUrl, { url, type })).asElement();
  782. const result = (await context.evaluateHandle(addScriptContent, { content, type })).asElement();
  783. if (this._page.delegate.cspErrorsAsynchronousForInlineScripts)
  784. await context.evaluate(() => true);
  785. return result;
  786. });
  787. async function addScriptUrl(params2) {
  788. const script = document.createElement("script");
  789. script.src = params2.url;
  790. if (params2.type)
  791. script.type = params2.type;
  792. const promise = new Promise((res, rej) => {
  793. script.onload = res;
  794. script.onerror = (e) => rej(typeof e === "string" ? new Error(e) : new Error(`Failed to load script at ${script.src}`));
  795. });
  796. document.head.appendChild(script);
  797. await promise;
  798. return script;
  799. }
  800. function addScriptContent(params2) {
  801. const script = document.createElement("script");
  802. script.type = params2.type || "text/javascript";
  803. script.text = params2.content;
  804. let error = null;
  805. script.onerror = (e) => error = e;
  806. document.head.appendChild(script);
  807. if (error)
  808. throw error;
  809. return script;
  810. }
  811. }
  812. async addStyleTag(params) {
  813. const {
  814. url = null,
  815. content = null
  816. } = params;
  817. if (!url && !content)
  818. throw new Error("Provide an object with a `url`, `path` or `content` property");
  819. const context = await this._mainContext();
  820. return this._raceWithCSPError(async () => {
  821. if (url !== null)
  822. return (await context.evaluateHandle(addStyleUrl, url)).asElement();
  823. return (await context.evaluateHandle(addStyleContent, content)).asElement();
  824. });
  825. async function addStyleUrl(url2) {
  826. const link = document.createElement("link");
  827. link.rel = "stylesheet";
  828. link.href = url2;
  829. const promise = new Promise((res, rej) => {
  830. link.onload = res;
  831. link.onerror = rej;
  832. });
  833. document.head.appendChild(link);
  834. await promise;
  835. return link;
  836. }
  837. async function addStyleContent(content2) {
  838. const style = document.createElement("style");
  839. style.type = "text/css";
  840. style.appendChild(document.createTextNode(content2));
  841. const promise = new Promise((res, rej) => {
  842. style.onload = res;
  843. style.onerror = rej;
  844. });
  845. document.head.appendChild(style);
  846. await promise;
  847. return style;
  848. }
  849. }
  850. async _raceWithCSPError(func) {
  851. const listeners = [];
  852. let result;
  853. let error;
  854. let cspMessage;
  855. const actionPromise = func().then((r) => result = r).catch((e) => error = e);
  856. const errorPromise = new Promise((resolve) => {
  857. listeners.push(import_eventsHelper.eventsHelper.addEventListener(this._page.browserContext, import_browserContext.BrowserContext.Events.Console, (message) => {
  858. if (message.page() !== this._page || message.type() !== "error")
  859. return;
  860. if (message.text().includes("Content-Security-Policy") || message.text().includes("Content Security Policy")) {
  861. cspMessage = message;
  862. resolve();
  863. }
  864. }));
  865. });
  866. await Promise.race([actionPromise, errorPromise]);
  867. import_eventsHelper.eventsHelper.removeEventListeners(listeners);
  868. if (cspMessage)
  869. throw new Error(cspMessage.text());
  870. if (error)
  871. throw error;
  872. return result;
  873. }
  874. async retryWithProgressAndTimeouts(progress, timeouts, action) {
  875. const continuePolling = Symbol("continuePolling");
  876. timeouts = [0, ...timeouts];
  877. let timeoutIndex = 0;
  878. while (true) {
  879. const timeout = timeouts[Math.min(timeoutIndex++, timeouts.length - 1)];
  880. if (timeout) {
  881. const actionPromise = new Promise((f) => setTimeout(f, timeout));
  882. await progress.race(import_utils.LongStandingScope.raceMultiple([
  883. this._page.openScope,
  884. this._detachedScope
  885. ], actionPromise));
  886. }
  887. try {
  888. const result = await action(continuePolling);
  889. if (result === continuePolling)
  890. continue;
  891. return result;
  892. } catch (e) {
  893. if (this.isNonRetriableError(e))
  894. throw e;
  895. continue;
  896. }
  897. }
  898. }
  899. isNonRetriableError(e) {
  900. if ((0, import_progress.isAbortError)(e))
  901. return true;
  902. if (js.isJavaScriptErrorInEvaluate(e) || (0, import_protocolError.isSessionClosedError)(e))
  903. return true;
  904. if (dom.isNonRecoverableDOMError(e) || (0, import_selectorParser.isInvalidSelectorError)(e))
  905. return true;
  906. if (this.isDetached())
  907. return true;
  908. return false;
  909. }
  910. async _retryWithProgressIfNotConnected(progress, selector, options, action) {
  911. progress.log(`waiting for ${this._asLocator(selector)}`);
  912. const noAutoWaiting = options.__testHookNoAutoWaiting ?? options.noAutoWaiting;
  913. const performActionPreChecks = (options.performActionPreChecks ?? !options.force) && !noAutoWaiting;
  914. return this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async (continuePolling) => {
  915. if (performActionPreChecks)
  916. await this._page.performActionPreChecks(progress);
  917. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, { strict: options.strict }));
  918. if (!resolved) {
  919. if (noAutoWaiting)
  920. throw new dom.NonRecoverableDOMError("Element(s) not found");
  921. return continuePolling;
  922. }
  923. const result = await progress.race(resolved.injected.evaluateHandle((injected, { info, callId }) => {
  924. const elements = injected.querySelectorAll(info.parsed, document);
  925. if (callId)
  926. injected.markTargetElements(new Set(elements), callId);
  927. const element2 = elements[0];
  928. let log2 = "";
  929. if (elements.length > 1) {
  930. if (info.strict)
  931. throw injected.strictModeViolationError(info.parsed, elements);
  932. log2 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`;
  933. } else if (element2) {
  934. log2 = ` locator resolved to ${injected.previewNode(element2)}`;
  935. }
  936. injected.checkDeprecatedSelectorUsage(info.parsed, elements);
  937. return { log: log2, success: !!element2, element: element2 };
  938. }, { info: resolved.info, callId: progress.metadata.id }));
  939. const { log, success } = await progress.race(result.evaluate((r) => ({ log: r.log, success: r.success })));
  940. if (log)
  941. progress.log(log);
  942. if (!success) {
  943. if (noAutoWaiting)
  944. throw new dom.NonRecoverableDOMError("Element(s) not found");
  945. result.dispose();
  946. return continuePolling;
  947. }
  948. const element = await progress.race(result.evaluateHandle((r) => r.element));
  949. result.dispose();
  950. try {
  951. const result2 = await action(element);
  952. if (result2 === "error:notconnected") {
  953. if (noAutoWaiting)
  954. throw new dom.NonRecoverableDOMError("Element is not attached to the DOM");
  955. progress.log("element was detached from the DOM, retrying");
  956. return continuePolling;
  957. }
  958. return result2;
  959. } finally {
  960. element?.dispose();
  961. }
  962. });
  963. }
  964. async rafrafTimeoutScreenshotElementWithProgress(progress, selector, timeout, options) {
  965. return await this._retryWithProgressIfNotConnected(progress, selector, { strict: true, performActionPreChecks: true }, async (handle) => {
  966. await handle._frame.rafrafTimeout(progress, timeout);
  967. return await this._page.screenshotter.screenshotElement(progress, handle, options);
  968. });
  969. }
  970. async click(progress, selector, options) {
  971. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._click(progress, { ...options, waitAfter: !options.noWaitAfter })));
  972. }
  973. async dblclick(progress, selector, options) {
  974. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._dblclick(progress, options)));
  975. }
  976. async dragAndDrop(progress, source, target, options) {
  977. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, source, options, async (handle) => {
  978. return handle._retryPointerAction(progress, "move and down", false, async (point) => {
  979. await this._page.mouse.move(progress, point.x, point.y);
  980. await this._page.mouse.down(progress);
  981. }, {
  982. ...options,
  983. waitAfter: "disabled",
  984. position: options.sourcePosition
  985. });
  986. }));
  987. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, target, { ...options, performActionPreChecks: false }, async (handle) => {
  988. return handle._retryPointerAction(progress, "move and up", false, async (point) => {
  989. await this._page.mouse.move(progress, point.x, point.y, { steps: options.steps });
  990. await this._page.mouse.up(progress);
  991. }, {
  992. ...options,
  993. waitAfter: "disabled",
  994. position: options.targetPosition
  995. });
  996. }));
  997. }
  998. async tap(progress, selector, options) {
  999. if (!this._page.browserContext._options.hasTouch)
  1000. throw new Error("The page does not support tap. Use hasTouch context option to enable touch support.");
  1001. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._tap(progress, options)));
  1002. }
  1003. async fill(progress, selector, value, options) {
  1004. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._fill(progress, value, options)));
  1005. }
  1006. async focus(progress, selector, options) {
  1007. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._focus(progress)));
  1008. }
  1009. async blur(progress, selector, options) {
  1010. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._blur(progress)));
  1011. }
  1012. async resolveSelector(progress, selector, options = {}) {
  1013. const element = await progress.race(this.selectors.query(selector, options));
  1014. if (!element)
  1015. throw new Error(`No element matching ${selector}`);
  1016. const generated = await progress.race(element.evaluateInUtility(async ([injected, node]) => {
  1017. return injected.generateSelectorSimple(node);
  1018. }, {}));
  1019. if (!generated)
  1020. throw new Error(`Unable to generate locator for ${selector}`);
  1021. let frame = element._frame;
  1022. const result = [generated];
  1023. while (frame?.parentFrame()) {
  1024. const frameElement = await progress.race(frame.frameElement());
  1025. if (frameElement) {
  1026. const generated2 = await progress.race(frameElement.evaluateInUtility(async ([injected, node]) => {
  1027. return injected.generateSelectorSimple(node);
  1028. }, {}));
  1029. frameElement.dispose();
  1030. if (generated2 === "error:notconnected" || !generated2)
  1031. throw new Error(`Unable to generate locator for ${selector}`);
  1032. result.push(generated2);
  1033. }
  1034. frame = frame.parentFrame();
  1035. }
  1036. const resolvedSelector = result.reverse().join(" >> internal:control=enter-frame >> ");
  1037. return { resolvedSelector };
  1038. }
  1039. async textContent(progress, selector, options, scope) {
  1040. return this._callOnElementOnceMatches(progress, selector, (injected, element) => element.textContent, void 0, options, scope);
  1041. }
  1042. async innerText(progress, selector, options, scope) {
  1043. return this._callOnElementOnceMatches(progress, selector, (injectedScript, element) => {
  1044. if (element.namespaceURI !== "http://www.w3.org/1999/xhtml")
  1045. throw injectedScript.createStacklessError("Node is not an HTMLElement");
  1046. return element.innerText;
  1047. }, void 0, options, scope);
  1048. }
  1049. async innerHTML(progress, selector, options, scope) {
  1050. return this._callOnElementOnceMatches(progress, selector, (injected, element) => element.innerHTML, void 0, options, scope);
  1051. }
  1052. async getAttribute(progress, selector, name, options, scope) {
  1053. return this._callOnElementOnceMatches(progress, selector, (injected, element, data) => element.getAttribute(data.name), { name }, options, scope);
  1054. }
  1055. async inputValue(progress, selector, options, scope) {
  1056. return this._callOnElementOnceMatches(progress, selector, (injectedScript, node) => {
  1057. const element = injectedScript.retarget(node, "follow-label");
  1058. if (!element || element.nodeName !== "INPUT" && element.nodeName !== "TEXTAREA" && element.nodeName !== "SELECT")
  1059. throw injectedScript.createStacklessError("Node is not an <input>, <textarea> or <select> element");
  1060. return element.value;
  1061. }, void 0, options, scope);
  1062. }
  1063. async highlight(progress, selector) {
  1064. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector));
  1065. if (!resolved)
  1066. return;
  1067. return await progress.race(resolved.injected.evaluate((injected, { info }) => {
  1068. return injected.highlight(info.parsed);
  1069. }, { info: resolved.info }));
  1070. }
  1071. async hideHighlight() {
  1072. return this.raceAgainstEvaluationStallingEvents(async () => {
  1073. const context = await this._utilityContext();
  1074. const injectedScript = await context.injectedScript();
  1075. return await injectedScript.evaluate((injected) => {
  1076. return injected.hideHighlight();
  1077. });
  1078. });
  1079. }
  1080. async _elementState(progress, selector, state, options, scope) {
  1081. const result = await this._callOnElementOnceMatches(progress, selector, (injected, element, data) => {
  1082. return injected.elementState(element, data.state);
  1083. }, { state }, options, scope);
  1084. if (result.received === "error:notconnected")
  1085. dom.throwElementIsNotAttached();
  1086. return result.matches;
  1087. }
  1088. async isVisible(progress, selector, options = {}, scope) {
  1089. progress.log(` checking visibility of ${this._asLocator(selector)}`);
  1090. return await this.isVisibleInternal(progress, selector, options, scope);
  1091. }
  1092. async isVisibleInternal(progress, selector, options = {}, scope) {
  1093. try {
  1094. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, options, scope));
  1095. if (!resolved)
  1096. return false;
  1097. return await progress.race(resolved.injected.evaluate((injected, { info, root }) => {
  1098. const element = injected.querySelector(info.parsed, root || document, info.strict);
  1099. const state = element ? injected.elementState(element, "visible") : { matches: false, received: "error:notconnected" };
  1100. return state.matches;
  1101. }, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
  1102. } catch (e) {
  1103. if (this.isNonRetriableError(e))
  1104. throw e;
  1105. return false;
  1106. }
  1107. }
  1108. async isHidden(progress, selector, options = {}, scope) {
  1109. return !await this.isVisible(progress, selector, options, scope);
  1110. }
  1111. async isDisabled(progress, selector, options, scope) {
  1112. return this._elementState(progress, selector, "disabled", options, scope);
  1113. }
  1114. async isEnabled(progress, selector, options, scope) {
  1115. return this._elementState(progress, selector, "enabled", options, scope);
  1116. }
  1117. async isEditable(progress, selector, options, scope) {
  1118. return this._elementState(progress, selector, "editable", options, scope);
  1119. }
  1120. async isChecked(progress, selector, options, scope) {
  1121. return this._elementState(progress, selector, "checked", options, scope);
  1122. }
  1123. async hover(progress, selector, options) {
  1124. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._hover(progress, options)));
  1125. }
  1126. async selectOption(progress, selector, elements, values, options) {
  1127. return await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._selectOption(progress, elements, values, options));
  1128. }
  1129. async setInputFiles(progress, selector, params) {
  1130. const inputFileItems = await (0, import_fileUploadUtils.prepareFilesForUpload)(this, params);
  1131. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, params, (handle) => handle._setInputFiles(progress, inputFileItems)));
  1132. }
  1133. async type(progress, selector, text, options) {
  1134. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._type(progress, text, options)));
  1135. }
  1136. async press(progress, selector, key, options) {
  1137. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._press(progress, key, options)));
  1138. }
  1139. async check(progress, selector, options) {
  1140. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._setChecked(progress, true, options)));
  1141. }
  1142. async uncheck(progress, selector, options) {
  1143. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (handle) => handle._setChecked(progress, false, options)));
  1144. }
  1145. async waitForTimeout(progress, timeout) {
  1146. return progress.wait(timeout);
  1147. }
  1148. async ariaSnapshot(progress, selector) {
  1149. return await this._retryWithProgressIfNotConnected(progress, selector, { strict: true, performActionPreChecks: true }, (handle) => progress.race(handle.ariaSnapshot()));
  1150. }
  1151. async expect(progress, selector, options) {
  1152. progress.log(`${(0, import_utils.renderTitleForCall)(progress.metadata)}${options.timeoutForLogs ? ` with timeout ${options.timeoutForLogs}ms` : ""}`);
  1153. const lastIntermediateResult = { isSet: false };
  1154. const fixupMetadataError = (result) => {
  1155. if (result.matches === options.isNot)
  1156. progress.metadata.error = { error: { name: "Expect", message: "Expect failed" } };
  1157. };
  1158. try {
  1159. if (selector)
  1160. progress.log(`waiting for ${this._asLocator(selector)}`);
  1161. if (!options.noAutoWaiting)
  1162. await this._page.performActionPreChecks(progress);
  1163. try {
  1164. const resultOneShot = await this._expectInternal(progress, selector, options, lastIntermediateResult, true);
  1165. if (options.noAutoWaiting || resultOneShot.matches !== options.isNot)
  1166. return resultOneShot;
  1167. } catch (e) {
  1168. if (options.noAutoWaiting || this.isNonRetriableError(e))
  1169. throw e;
  1170. }
  1171. const result = await this.retryWithProgressAndTimeouts(progress, [100, 250, 500, 1e3], async (continuePolling) => {
  1172. if (!options.noAutoWaiting)
  1173. await this._page.performActionPreChecks(progress);
  1174. const { matches, received } = await this._expectInternal(progress, selector, options, lastIntermediateResult, false);
  1175. if (matches === options.isNot) {
  1176. return continuePolling;
  1177. }
  1178. return { matches, received };
  1179. });
  1180. fixupMetadataError(result);
  1181. return result;
  1182. } catch (e) {
  1183. const result = { matches: options.isNot, log: (0, import_callLog.compressCallLog)(progress.metadata.log) };
  1184. if ((0, import_selectorParser.isInvalidSelectorError)(e)) {
  1185. result.errorMessage = "Error: " + e.message;
  1186. } else if (js.isJavaScriptErrorInEvaluate(e)) {
  1187. result.errorMessage = e.message;
  1188. } else if (lastIntermediateResult.isSet) {
  1189. result.received = lastIntermediateResult.received;
  1190. result.errorMessage = lastIntermediateResult.errorMessage;
  1191. }
  1192. if (e instanceof import_errors.TimeoutError)
  1193. result.timedOut = true;
  1194. fixupMetadataError(result);
  1195. return result;
  1196. }
  1197. }
  1198. async _expectInternal(progress, selector, options, lastIntermediateResult, noAbort) {
  1199. const race = (p) => noAbort ? p : progress.race(p);
  1200. const selectorInFrame = selector ? await race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0;
  1201. const { frame, info } = selectorInFrame || { frame: this, info: void 0 };
  1202. const world = options.expression === "to.have.property" ? "main" : info?.world ?? "utility";
  1203. const context = await race(frame._context(world));
  1204. const injected = await race(context.injectedScript());
  1205. const { log, matches, received, missingReceived } = await race(injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
  1206. const elements = info2 ? injected2.querySelectorAll(info2.parsed, document) : [];
  1207. if (callId)
  1208. injected2.markTargetElements(new Set(elements), callId);
  1209. const isArray = options2.expression === "to.have.count" || options2.expression.endsWith(".array");
  1210. let log2 = "";
  1211. if (isArray)
  1212. log2 = ` locator resolved to ${elements.length} element${elements.length === 1 ? "" : "s"}`;
  1213. else if (elements.length > 1)
  1214. throw injected2.strictModeViolationError(info2.parsed, elements);
  1215. else if (elements.length)
  1216. log2 = ` locator resolved to ${injected2.previewNode(elements[0])}`;
  1217. if (info2)
  1218. injected2.checkDeprecatedSelectorUsage(info2.parsed, elements);
  1219. return { log: log2, ...await injected2.expect(elements[0], options2, elements) };
  1220. }, { info, options, callId: progress.metadata.id }));
  1221. if (log)
  1222. progress.log(log);
  1223. if (matches === options.isNot) {
  1224. if (missingReceived) {
  1225. lastIntermediateResult.errorMessage = "Error: element(s) not found";
  1226. } else {
  1227. lastIntermediateResult.errorMessage = void 0;
  1228. lastIntermediateResult.received = received;
  1229. }
  1230. lastIntermediateResult.isSet = true;
  1231. if (!missingReceived && !Array.isArray(received))
  1232. progress.log(` unexpected value "${renderUnexpectedValue(options.expression, received)}"`);
  1233. }
  1234. return { matches, received };
  1235. }
  1236. async waitForFunctionExpression(progress, expression, isFunction, arg, options, world = "main") {
  1237. if (typeof options.pollingInterval === "number")
  1238. (0, import_utils.assert)(options.pollingInterval > 0, "Cannot poll with non-positive interval: " + options.pollingInterval);
  1239. expression = js.normalizeEvaluationExpression(expression, isFunction);
  1240. return this.retryWithProgressAndTimeouts(progress, [100], async () => {
  1241. const context = world === "main" ? await progress.race(this._mainContext()) : await progress.race(this._utilityContext());
  1242. const injectedScript = await progress.race(context.injectedScript());
  1243. const handle = await progress.race(injectedScript.evaluateHandle((injected, { expression: expression2, isFunction: isFunction2, polling, arg: arg2 }) => {
  1244. let evaledExpression;
  1245. const predicate = () => {
  1246. let result2 = evaledExpression ?? globalThis.eval(expression2);
  1247. if (isFunction2 === true) {
  1248. evaledExpression = result2;
  1249. result2 = result2(arg2);
  1250. } else if (isFunction2 === false) {
  1251. result2 = result2;
  1252. } else {
  1253. if (typeof result2 === "function") {
  1254. evaledExpression = result2;
  1255. result2 = result2(arg2);
  1256. }
  1257. }
  1258. return result2;
  1259. };
  1260. let fulfill;
  1261. let reject;
  1262. let aborted = false;
  1263. const result = new Promise((f, r) => {
  1264. fulfill = f;
  1265. reject = r;
  1266. });
  1267. const next = () => {
  1268. if (aborted)
  1269. return;
  1270. try {
  1271. const success = predicate();
  1272. if (success) {
  1273. fulfill(success);
  1274. return;
  1275. }
  1276. if (typeof polling !== "number")
  1277. injected.utils.builtins.requestAnimationFrame(next);
  1278. else
  1279. injected.utils.builtins.setTimeout(next, polling);
  1280. } catch (e) {
  1281. reject(e);
  1282. }
  1283. };
  1284. next();
  1285. return { result, abort: () => aborted = true };
  1286. }, { expression, isFunction, polling: options.pollingInterval, arg }));
  1287. try {
  1288. return await progress.race(handle.evaluateHandle((h) => h.result));
  1289. } catch (error) {
  1290. await handle.evaluate((h) => h.abort()).catch(() => {
  1291. });
  1292. throw error;
  1293. } finally {
  1294. handle.dispose();
  1295. }
  1296. });
  1297. }
  1298. async waitForFunctionValueInUtility(progress, pageFunction) {
  1299. const expression = `() => {
  1300. const result = (${pageFunction})();
  1301. if (!result)
  1302. return result;
  1303. return JSON.stringify(result);
  1304. }`;
  1305. const handle = await this.waitForFunctionExpression(progress, expression, true, void 0, {}, "utility");
  1306. return JSON.parse(handle.rawValue());
  1307. }
  1308. async title() {
  1309. const context = await this._utilityContext();
  1310. return context.evaluate(() => document.title);
  1311. }
  1312. async rafrafTimeout(progress, timeout) {
  1313. if (timeout === 0)
  1314. return;
  1315. const context = await progress.race(this._utilityContext());
  1316. await Promise.all([
  1317. // wait for double raf
  1318. progress.race(context.evaluate(() => new Promise((x) => {
  1319. requestAnimationFrame(() => {
  1320. requestAnimationFrame(x);
  1321. });
  1322. }))),
  1323. progress.wait(timeout)
  1324. ]);
  1325. }
  1326. _onDetached() {
  1327. this._stopNetworkIdleTimer();
  1328. this._detachedScope.close(new Error("Frame was detached"));
  1329. for (const data of this._contextData.values()) {
  1330. if (data.context)
  1331. data.context.contextDestroyed("Frame was detached");
  1332. data.contextPromise.resolve({ destroyedReason: "Frame was detached" });
  1333. }
  1334. if (this._parentFrame)
  1335. this._parentFrame._childFrames.delete(this);
  1336. this._parentFrame = null;
  1337. }
  1338. async _callOnElementOnceMatches(progress, selector, body, taskData, options, scope) {
  1339. const callbackText = body.toString();
  1340. progress.log(`waiting for ${this._asLocator(selector)}`);
  1341. const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async (continuePolling) => {
  1342. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, options, scope));
  1343. if (!resolved)
  1344. return continuePolling;
  1345. const { log, success, value } = await progress.race(resolved.injected.evaluate((injected, { info, callbackText: callbackText2, taskData: taskData2, callId, root }) => {
  1346. const callback = injected.eval(callbackText2);
  1347. const element = injected.querySelector(info.parsed, root || document, info.strict);
  1348. if (!element)
  1349. return { success: false };
  1350. const log2 = ` locator resolved to ${injected.previewNode(element)}`;
  1351. if (callId)
  1352. injected.markTargetElements(/* @__PURE__ */ new Set([element]), callId);
  1353. return { log: log2, success: true, value: callback(injected, element, taskData2) };
  1354. }, { info: resolved.info, callbackText, taskData, callId: progress.metadata.id, root: resolved.frame === this ? scope : void 0 }));
  1355. if (log)
  1356. progress.log(log);
  1357. if (!success)
  1358. return continuePolling;
  1359. return value;
  1360. });
  1361. return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise;
  1362. }
  1363. _setContext(world, context) {
  1364. const data = this._contextData.get(world);
  1365. data.context = context;
  1366. if (context)
  1367. data.contextPromise.resolve(context);
  1368. else
  1369. data.contextPromise = new import_manualPromise.ManualPromise();
  1370. }
  1371. _contextCreated(world, context) {
  1372. const data = this._contextData.get(world);
  1373. if (data.context) {
  1374. data.context.contextDestroyed("Execution context was destroyed, most likely because of a navigation");
  1375. this._setContext(world, null);
  1376. }
  1377. this._setContext(world, context);
  1378. }
  1379. _contextDestroyed(context) {
  1380. if (this._detachedScope.isClosed())
  1381. return;
  1382. context.contextDestroyed("Execution context was destroyed, most likely because of a navigation");
  1383. for (const [world, data] of this._contextData) {
  1384. if (data.context === context)
  1385. this._setContext(world, null);
  1386. }
  1387. }
  1388. _startNetworkIdleTimer() {
  1389. (0, import_utils.assert)(!this._networkIdleTimer);
  1390. if (this._firedLifecycleEvents.has("networkidle") || this._detachedScope.isClosed())
  1391. return;
  1392. this._networkIdleTimer = setTimeout(() => {
  1393. this._firedNetworkIdleSelf = true;
  1394. this._page.mainFrame()._recalculateNetworkIdle();
  1395. }, 500);
  1396. }
  1397. _stopNetworkIdleTimer() {
  1398. if (this._networkIdleTimer)
  1399. clearTimeout(this._networkIdleTimer);
  1400. this._networkIdleTimer = void 0;
  1401. this._firedNetworkIdleSelf = false;
  1402. }
  1403. async extendInjectedScript(source, arg) {
  1404. const context = await this._context("main");
  1405. const injectedScriptHandle = await context.injectedScript();
  1406. await injectedScriptHandle.evaluate((injectedScript, { source: source2, arg: arg2 }) => {
  1407. injectedScript.extend(source2, arg2);
  1408. }, { source, arg });
  1409. }
  1410. _asLocator(selector) {
  1411. return (0, import_utils.asLocator)(this._page.browserContext._browser.sdkLanguage(), selector);
  1412. }
  1413. }
  1414. class SignalBarrier {
  1415. constructor(progress) {
  1416. this._protectCount = 0;
  1417. this._promise = new import_manualPromise.ManualPromise();
  1418. this._progress = progress;
  1419. this.retain();
  1420. }
  1421. waitFor() {
  1422. this.release();
  1423. return this._progress.race(this._promise);
  1424. }
  1425. addFrameNavigation(frame) {
  1426. if (frame.parentFrame())
  1427. return;
  1428. this.retain();
  1429. const waiter = import_helper.helper.waitForEvent(this._progress, frame, Frame.Events.InternalNavigation, (e) => {
  1430. if (!e.isPublic)
  1431. return false;
  1432. if (!e.error && this._progress)
  1433. this._progress.log(` navigated to "${frame._url}"`);
  1434. return true;
  1435. });
  1436. import_utils.LongStandingScope.raceMultiple([
  1437. frame._page.openScope,
  1438. frame._detachedScope
  1439. ], waiter.promise).catch(() => {
  1440. }).finally(() => {
  1441. waiter.dispose();
  1442. this.release();
  1443. });
  1444. }
  1445. retain() {
  1446. ++this._protectCount;
  1447. }
  1448. release() {
  1449. --this._protectCount;
  1450. if (!this._protectCount)
  1451. this._promise.resolve();
  1452. }
  1453. }
  1454. function verifyLifecycle(name, waitUntil) {
  1455. if (waitUntil === "networkidle0")
  1456. waitUntil = "networkidle";
  1457. if (!types.kLifecycleEvents.has(waitUntil))
  1458. throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
  1459. return waitUntil;
  1460. }
  1461. function renderUnexpectedValue(expression, received) {
  1462. if (expression === "to.match.aria")
  1463. return received ? received.raw : received;
  1464. return received;
  1465. }
  1466. // Annotate the CommonJS export names for ESM import in node:
  1467. 0 && (module.exports = {
  1468. Frame,
  1469. FrameManager,
  1470. NavigationAbortedError
  1471. });