frames.js 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  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. class Frame extends import_instrumentation.SdkObject {
  354. constructor(page, id, parentFrame) {
  355. super(page, "frame");
  356. this._firedLifecycleEvents = /* @__PURE__ */ new Set();
  357. this._firedNetworkIdleSelf = false;
  358. this._url = "";
  359. this._contextData = /* @__PURE__ */ new Map();
  360. this._childFrames = /* @__PURE__ */ new Set();
  361. this._name = "";
  362. this._inflightRequests = /* @__PURE__ */ new Set();
  363. this._setContentCounter = 0;
  364. this._detachedScope = new import_utils.LongStandingScope();
  365. this._raceAgainstEvaluationStallingEventsPromises = /* @__PURE__ */ new Set();
  366. this._redirectedNavigations = /* @__PURE__ */ new Map();
  367. this.attribution.frame = this;
  368. this.seq = page.frameManager.nextFrameSeq();
  369. this._id = id;
  370. this._page = page;
  371. this._parentFrame = parentFrame;
  372. this._currentDocument = { documentId: void 0, request: void 0 };
  373. this.selectors = new import_frameSelectors.FrameSelectors(this);
  374. this._contextData.set("main", { contextPromise: new import_manualPromise.ManualPromise(), context: null });
  375. this._contextData.set("utility", { contextPromise: new import_manualPromise.ManualPromise(), context: null });
  376. this._setContext("main", null);
  377. this._setContext("utility", null);
  378. if (this._parentFrame)
  379. this._parentFrame._childFrames.add(this);
  380. this._firedLifecycleEvents.add("commit");
  381. if (id !== kDummyFrameId)
  382. this._startNetworkIdleTimer();
  383. }
  384. static {
  385. this.Events = {
  386. InternalNavigation: "internalnavigation",
  387. AddLifecycle: "addlifecycle",
  388. RemoveLifecycle: "removelifecycle"
  389. };
  390. }
  391. isDetached() {
  392. return this._detachedScope.isClosed();
  393. }
  394. _onLifecycleEvent(event) {
  395. if (this._firedLifecycleEvents.has(event))
  396. return;
  397. this._firedLifecycleEvents.add(event);
  398. this.emit(Frame.Events.AddLifecycle, event);
  399. if (this === this._page.mainFrame() && this._url !== "about:blank")
  400. import_debugLogger.debugLogger.log("api", ` "${event}" event fired`);
  401. this._page.mainFrame()._recalculateNetworkIdle();
  402. }
  403. _onClearLifecycle() {
  404. for (const event of this._firedLifecycleEvents)
  405. this.emit(Frame.Events.RemoveLifecycle, event);
  406. this._firedLifecycleEvents.clear();
  407. this._inflightRequests = new Set(Array.from(this._inflightRequests).filter((request) => request === this._currentDocument.request));
  408. this._stopNetworkIdleTimer();
  409. if (this._inflightRequests.size === 0)
  410. this._startNetworkIdleTimer();
  411. this._page.mainFrame()._recalculateNetworkIdle(this);
  412. this._onLifecycleEvent("commit");
  413. }
  414. setPendingDocument(documentInfo) {
  415. this._pendingDocument = documentInfo;
  416. if (documentInfo)
  417. this._invalidateNonStallingEvaluations("Navigation interrupted the evaluation");
  418. }
  419. pendingDocument() {
  420. return this._pendingDocument;
  421. }
  422. _invalidateNonStallingEvaluations(message) {
  423. if (!this._raceAgainstEvaluationStallingEventsPromises.size)
  424. return;
  425. const error = new Error(message);
  426. for (const promise of this._raceAgainstEvaluationStallingEventsPromises)
  427. promise.reject(error);
  428. }
  429. async raceAgainstEvaluationStallingEvents(cb) {
  430. if (this._pendingDocument)
  431. throw new Error("Frame is currently attempting a navigation");
  432. if (this._page.browserContext.dialogManager.hasOpenDialogsForPage(this._page))
  433. throw new Error("Open JavaScript dialog prevents evaluation");
  434. const promise = new import_manualPromise.ManualPromise();
  435. this._raceAgainstEvaluationStallingEventsPromises.add(promise);
  436. try {
  437. return await Promise.race([
  438. cb(),
  439. promise
  440. ]);
  441. } finally {
  442. this._raceAgainstEvaluationStallingEventsPromises.delete(promise);
  443. }
  444. }
  445. nonStallingRawEvaluateInExistingMainContext(expression) {
  446. return this.raceAgainstEvaluationStallingEvents(() => {
  447. const context = this._existingMainContext();
  448. if (!context)
  449. throw new Error("Frame does not yet have a main execution context");
  450. return context.rawEvaluateJSON(expression);
  451. });
  452. }
  453. nonStallingEvaluateInExistingContext(expression, world) {
  454. return this.raceAgainstEvaluationStallingEvents(() => {
  455. const context = this._contextData.get(world)?.context;
  456. if (!context)
  457. throw new Error("Frame does not yet have the execution context");
  458. return context.evaluateExpression(expression, { isFunction: false });
  459. });
  460. }
  461. _recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle) {
  462. let isNetworkIdle = this._firedNetworkIdleSelf;
  463. for (const child of this._childFrames) {
  464. child._recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle);
  465. if (!child._firedLifecycleEvents.has("networkidle"))
  466. isNetworkIdle = false;
  467. }
  468. if (isNetworkIdle && !this._firedLifecycleEvents.has("networkidle")) {
  469. this._firedLifecycleEvents.add("networkidle");
  470. this.emit(Frame.Events.AddLifecycle, "networkidle");
  471. if (this === this._page.mainFrame() && this._url !== "about:blank")
  472. import_debugLogger.debugLogger.log("api", ` "networkidle" event fired`);
  473. }
  474. if (frameThatAllowsRemovingNetworkIdle !== this && this._firedLifecycleEvents.has("networkidle") && !isNetworkIdle) {
  475. this._firedLifecycleEvents.delete("networkidle");
  476. this.emit(Frame.Events.RemoveLifecycle, "networkidle");
  477. }
  478. }
  479. async raceNavigationAction(progress, action) {
  480. return import_utils.LongStandingScope.raceMultiple([
  481. this._detachedScope,
  482. this._page.openScope
  483. ], action().catch((e) => {
  484. if (e instanceof NavigationAbortedError && e.documentId) {
  485. const data = this._redirectedNavigations.get(e.documentId);
  486. if (data) {
  487. progress.log(`waiting for redirected navigation to "${data.url}"`);
  488. return progress.race(data.gotoPromise);
  489. }
  490. }
  491. throw e;
  492. }));
  493. }
  494. redirectNavigation(url, documentId, referer) {
  495. const controller = new import_progress.ProgressController();
  496. const data = {
  497. url,
  498. gotoPromise: controller.run((progress) => this.gotoImpl(progress, url, { referer }), 0)
  499. };
  500. this._redirectedNavigations.set(documentId, data);
  501. data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId));
  502. }
  503. async goto(progress, url, options = {}) {
  504. const constructedNavigationURL = (0, import_utils.constructURLBasedOnBaseURL)(this._page.browserContext._options.baseURL, url);
  505. return this.raceNavigationAction(progress, async () => this.gotoImpl(progress, constructedNavigationURL, options));
  506. }
  507. async gotoImpl(progress, url, options) {
  508. const waitUntil = verifyLifecycle("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
  509. progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
  510. const headers = this._page.extraHTTPHeaders() || [];
  511. const refererHeader = headers.find((h) => h.name.toLowerCase() === "referer");
  512. let referer = refererHeader ? refererHeader.value : void 0;
  513. if (options.referer !== void 0) {
  514. if (referer !== void 0 && referer !== options.referer)
  515. throw new Error('"referer" is already specified as extra HTTP header');
  516. referer = options.referer;
  517. }
  518. url = import_helper.helper.completeUserURL(url);
  519. const navigationEvents = [];
  520. const collectNavigations = (arg) => navigationEvents.push(arg);
  521. this.on(Frame.Events.InternalNavigation, collectNavigations);
  522. const navigateResult = await progress.race(this._page.delegate.navigateFrame(this, url, referer)).finally(
  523. () => this.off(Frame.Events.InternalNavigation, collectNavigations)
  524. );
  525. let event;
  526. if (navigateResult.newDocumentId) {
  527. const predicate = (event2) => {
  528. return event2.newDocument && (event2.newDocument.documentId === navigateResult.newDocumentId || !event2.error);
  529. };
  530. const events = navigationEvents.filter(predicate);
  531. if (events.length)
  532. event = events[0];
  533. else
  534. event = await import_helper.helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, predicate).promise;
  535. if (event.newDocument.documentId !== navigateResult.newDocumentId) {
  536. throw new NavigationAbortedError(navigateResult.newDocumentId, `Navigation to "${url}" is interrupted by another navigation to "${event.url}"`);
  537. }
  538. if (event.error)
  539. throw event.error;
  540. } else {
  541. const predicate = (e) => !e.newDocument;
  542. const events = navigationEvents.filter(predicate);
  543. if (events.length)
  544. event = events[0];
  545. else
  546. event = await import_helper.helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, predicate).promise;
  547. }
  548. if (!this._firedLifecycleEvents.has(waitUntil))
  549. await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
  550. const request = event.newDocument ? event.newDocument.request : void 0;
  551. const response = request ? progress.race(request._finalRequest().response()) : null;
  552. return response;
  553. }
  554. async _waitForNavigation(progress, requiresNewDocument, options) {
  555. const waitUntil = verifyLifecycle("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
  556. progress.log(`waiting for navigation until "${waitUntil}"`);
  557. const navigationEvent = await import_helper.helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (event) => {
  558. if (event.error)
  559. return true;
  560. if (requiresNewDocument && !event.newDocument)
  561. return false;
  562. progress.log(` navigated to "${this._url}"`);
  563. return true;
  564. }).promise;
  565. if (navigationEvent.error)
  566. throw navigationEvent.error;
  567. if (!this._firedLifecycleEvents.has(waitUntil))
  568. await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
  569. const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : void 0;
  570. return request ? progress.race(request._finalRequest().response()) : null;
  571. }
  572. async _waitForLoadState(progress, state) {
  573. const waitUntil = verifyLifecycle("state", state);
  574. if (!this._firedLifecycleEvents.has(waitUntil))
  575. await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
  576. }
  577. async frameElement() {
  578. return this._page.delegate.getFrameElement(this);
  579. }
  580. _context(world) {
  581. return this._contextData.get(world).contextPromise.then((contextOrDestroyedReason) => {
  582. if (contextOrDestroyedReason instanceof js.ExecutionContext)
  583. return contextOrDestroyedReason;
  584. throw new Error(contextOrDestroyedReason.destroyedReason);
  585. });
  586. }
  587. _mainContext() {
  588. return this._context("main");
  589. }
  590. _existingMainContext() {
  591. return this._contextData.get("main")?.context || null;
  592. }
  593. _utilityContext() {
  594. return this._context("utility");
  595. }
  596. async evaluateExpression(expression, options = {}, arg) {
  597. const context = await this._context(options.world ?? "main");
  598. const value = await context.evaluateExpression(expression, options, arg);
  599. return value;
  600. }
  601. async evaluateExpressionHandle(expression, options = {}, arg) {
  602. const context = await this._context(options.world ?? "main");
  603. const value = await context.evaluateExpressionHandle(expression, options, arg);
  604. return value;
  605. }
  606. async querySelector(selector, options) {
  607. import_debugLogger.debugLogger.log("api", ` finding element using the selector "${selector}"`);
  608. return this.selectors.query(selector, options);
  609. }
  610. async waitForSelector(progress, selector, performActionPreChecksAndLog, options, scope) {
  611. if (options.visibility)
  612. throw new Error("options.visibility is not supported, did you mean options.state?");
  613. if (options.waitFor && options.waitFor !== "visible")
  614. throw new Error("options.waitFor is not supported, did you mean options.state?");
  615. const { state = "visible" } = options;
  616. if (!["attached", "detached", "visible", "hidden"].includes(state))
  617. throw new Error(`state: expected one of (attached|detached|visible|hidden)`);
  618. if (performActionPreChecksAndLog)
  619. progress.log(`waiting for ${this._asLocator(selector)}${state === "attached" ? "" : " to be " + state}`);
  620. const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async (continuePolling) => {
  621. if (performActionPreChecksAndLog)
  622. await this._page.performActionPreChecks(progress);
  623. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, options, scope));
  624. if (!resolved) {
  625. if (state === "hidden" || state === "detached")
  626. return null;
  627. return continuePolling;
  628. }
  629. const result = await progress.race(resolved.injected.evaluateHandle((injected, { info, root }) => {
  630. if (root && !root.isConnected)
  631. throw injected.createStacklessError("Element is not attached to the DOM");
  632. const elements = injected.querySelectorAll(info.parsed, root || document);
  633. const element2 = elements[0];
  634. const visible2 = element2 ? injected.utils.isElementVisible(element2) : false;
  635. let log2 = "";
  636. if (elements.length > 1) {
  637. if (info.strict)
  638. throw injected.strictModeViolationError(info.parsed, elements);
  639. log2 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`;
  640. } else if (element2) {
  641. log2 = ` locator resolved to ${visible2 ? "visible" : "hidden"} ${injected.previewNode(element2)}`;
  642. }
  643. return { log: log2, element: element2, visible: visible2, attached: !!element2 };
  644. }, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
  645. const { log, visible, attached } = await progress.race(result.evaluate((r) => ({ log: r.log, visible: r.visible, attached: r.attached })));
  646. if (log)
  647. progress.log(log);
  648. const success = { attached, detached: !attached, visible, hidden: !visible }[state];
  649. if (!success) {
  650. result.dispose();
  651. return continuePolling;
  652. }
  653. if (options.omitReturnValue) {
  654. result.dispose();
  655. return null;
  656. }
  657. const element = state === "attached" || state === "visible" ? await progress.race(result.evaluateHandle((r) => r.element)) : null;
  658. result.dispose();
  659. if (!element)
  660. return null;
  661. if (options.__testHookBeforeAdoptNode)
  662. await progress.race(options.__testHookBeforeAdoptNode());
  663. try {
  664. const mainContext = await progress.race(resolved.frame._mainContext());
  665. return await progress.race(element._adoptTo(mainContext));
  666. } catch (e) {
  667. return continuePolling;
  668. }
  669. });
  670. return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise;
  671. }
  672. async dispatchEvent(progress, selector, type, eventInit = {}, options, scope) {
  673. await this._callOnElementOnceMatches(progress, selector, (injectedScript, element, data) => {
  674. injectedScript.dispatchEvent(element, data.type, data.eventInit);
  675. }, { type, eventInit }, { mainWorld: true, ...options }, scope);
  676. }
  677. async evalOnSelector(selector, strict, expression, isFunction, arg, scope) {
  678. const handle = await this.selectors.query(selector, { strict }, scope);
  679. if (!handle)
  680. throw new Error(`Failed to find element matching selector "${selector}"`);
  681. const result = await handle.evaluateExpression(expression, { isFunction }, arg);
  682. handle.dispose();
  683. return result;
  684. }
  685. async evalOnSelectorAll(selector, expression, isFunction, arg, scope) {
  686. const arrayHandle = await this.selectors.queryArrayInMainWorld(selector, scope);
  687. const result = await arrayHandle.evaluateExpression(expression, { isFunction }, arg);
  688. arrayHandle.dispose();
  689. return result;
  690. }
  691. async maskSelectors(selectors, color) {
  692. const context = await this._utilityContext();
  693. const injectedScript = await context.injectedScript();
  694. await injectedScript.evaluate((injected, { parsed, color: color2 }) => {
  695. injected.maskSelectors(parsed, color2);
  696. }, { parsed: selectors, color });
  697. }
  698. async querySelectorAll(selector) {
  699. return this.selectors.queryAll(selector);
  700. }
  701. async queryCount(selector, options) {
  702. try {
  703. return await this.selectors.queryCount(selector, options);
  704. } catch (e) {
  705. if (this.isNonRetriableError(e))
  706. throw e;
  707. return 0;
  708. }
  709. }
  710. async content() {
  711. try {
  712. const context = await this._utilityContext();
  713. return await context.evaluate(() => {
  714. let retVal = "";
  715. if (document.doctype)
  716. retVal = new XMLSerializer().serializeToString(document.doctype);
  717. if (document.documentElement)
  718. retVal += document.documentElement.outerHTML;
  719. return retVal;
  720. });
  721. } catch (e) {
  722. if (this.isNonRetriableError(e))
  723. throw e;
  724. throw new Error(`Unable to retrieve content because the page is navigating and changing the content.`);
  725. }
  726. }
  727. async setContent(progress, html, options) {
  728. const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
  729. await this.raceNavigationAction(progress, async () => {
  730. const waitUntil = options.waitUntil === void 0 ? "load" : options.waitUntil;
  731. progress.log(`setting frame content, waiting until "${waitUntil}"`);
  732. const context = await progress.race(this._utilityContext());
  733. const tagPromise = new import_manualPromise.ManualPromise();
  734. this._page.frameManager._consoleMessageTags.set(tag, () => {
  735. this._onClearLifecycle();
  736. tagPromise.resolve();
  737. });
  738. const lifecyclePromise = progress.race(tagPromise).then(() => this._waitForLoadState(progress, waitUntil));
  739. const contentPromise = progress.race(context.evaluate(({ html: html2, tag: tag2 }) => {
  740. document.open();
  741. console.debug(tag2);
  742. document.write(html2);
  743. document.close();
  744. }, { html, tag }));
  745. await Promise.all([contentPromise, lifecyclePromise]);
  746. return null;
  747. }).finally(() => {
  748. this._page.frameManager._consoleMessageTags.delete(tag);
  749. });
  750. }
  751. name() {
  752. return this._name || "";
  753. }
  754. url() {
  755. return this._url;
  756. }
  757. origin() {
  758. if (!this._url.startsWith("http"))
  759. return;
  760. return network.parseURL(this._url)?.origin;
  761. }
  762. parentFrame() {
  763. return this._parentFrame;
  764. }
  765. childFrames() {
  766. return Array.from(this._childFrames);
  767. }
  768. async addScriptTag(params) {
  769. const {
  770. url = null,
  771. content = null,
  772. type = ""
  773. } = params;
  774. if (!url && !content)
  775. throw new Error("Provide an object with a `url`, `path` or `content` property");
  776. const context = await this._mainContext();
  777. return this._raceWithCSPError(async () => {
  778. if (url !== null)
  779. return (await context.evaluateHandle(addScriptUrl, { url, type })).asElement();
  780. const result = (await context.evaluateHandle(addScriptContent, { content, type })).asElement();
  781. if (this._page.delegate.cspErrorsAsynchronousForInlineScripts)
  782. await context.evaluate(() => true);
  783. return result;
  784. });
  785. async function addScriptUrl(params2) {
  786. const script = document.createElement("script");
  787. script.src = params2.url;
  788. if (params2.type)
  789. script.type = params2.type;
  790. const promise = new Promise((res, rej) => {
  791. script.onload = res;
  792. script.onerror = (e) => rej(typeof e === "string" ? new Error(e) : new Error(`Failed to load script at ${script.src}`));
  793. });
  794. document.head.appendChild(script);
  795. await promise;
  796. return script;
  797. }
  798. function addScriptContent(params2) {
  799. const script = document.createElement("script");
  800. script.type = params2.type || "text/javascript";
  801. script.text = params2.content;
  802. let error = null;
  803. script.onerror = (e) => error = e;
  804. document.head.appendChild(script);
  805. if (error)
  806. throw error;
  807. return script;
  808. }
  809. }
  810. async addStyleTag(params) {
  811. const {
  812. url = null,
  813. content = null
  814. } = params;
  815. if (!url && !content)
  816. throw new Error("Provide an object with a `url`, `path` or `content` property");
  817. const context = await this._mainContext();
  818. return this._raceWithCSPError(async () => {
  819. if (url !== null)
  820. return (await context.evaluateHandle(addStyleUrl, url)).asElement();
  821. return (await context.evaluateHandle(addStyleContent, content)).asElement();
  822. });
  823. async function addStyleUrl(url2) {
  824. const link = document.createElement("link");
  825. link.rel = "stylesheet";
  826. link.href = url2;
  827. const promise = new Promise((res, rej) => {
  828. link.onload = res;
  829. link.onerror = rej;
  830. });
  831. document.head.appendChild(link);
  832. await promise;
  833. return link;
  834. }
  835. async function addStyleContent(content2) {
  836. const style = document.createElement("style");
  837. style.type = "text/css";
  838. style.appendChild(document.createTextNode(content2));
  839. const promise = new Promise((res, rej) => {
  840. style.onload = res;
  841. style.onerror = rej;
  842. });
  843. document.head.appendChild(style);
  844. await promise;
  845. return style;
  846. }
  847. }
  848. async _raceWithCSPError(func) {
  849. const listeners = [];
  850. let result;
  851. let error;
  852. let cspMessage;
  853. const actionPromise = func().then((r) => result = r).catch((e) => error = e);
  854. const errorPromise = new Promise((resolve) => {
  855. listeners.push(import_eventsHelper.eventsHelper.addEventListener(this._page.browserContext, import_browserContext.BrowserContext.Events.Console, (message) => {
  856. if (message.page() !== this._page || message.type() !== "error")
  857. return;
  858. if (message.text().includes("Content-Security-Policy") || message.text().includes("Content Security Policy")) {
  859. cspMessage = message;
  860. resolve();
  861. }
  862. }));
  863. });
  864. await Promise.race([actionPromise, errorPromise]);
  865. import_eventsHelper.eventsHelper.removeEventListeners(listeners);
  866. if (cspMessage)
  867. throw new Error(cspMessage.text());
  868. if (error)
  869. throw error;
  870. return result;
  871. }
  872. async retryWithProgressAndTimeouts(progress, timeouts, action) {
  873. const continuePolling = Symbol("continuePolling");
  874. timeouts = [0, ...timeouts];
  875. let timeoutIndex = 0;
  876. while (true) {
  877. const timeout = timeouts[Math.min(timeoutIndex++, timeouts.length - 1)];
  878. if (timeout) {
  879. const actionPromise = new Promise((f) => setTimeout(f, timeout));
  880. await progress.race(import_utils.LongStandingScope.raceMultiple([
  881. this._page.openScope,
  882. this._detachedScope
  883. ], actionPromise));
  884. }
  885. try {
  886. const result = await action(continuePolling);
  887. if (result === continuePolling)
  888. continue;
  889. return result;
  890. } catch (e) {
  891. if (this.isNonRetriableError(e))
  892. throw e;
  893. continue;
  894. }
  895. }
  896. }
  897. isNonRetriableError(e) {
  898. if ((0, import_progress.isAbortError)(e))
  899. return true;
  900. if (js.isJavaScriptErrorInEvaluate(e) || (0, import_protocolError.isSessionClosedError)(e))
  901. return true;
  902. if (dom.isNonRecoverableDOMError(e) || (0, import_selectorParser.isInvalidSelectorError)(e))
  903. return true;
  904. if (this.isDetached())
  905. return true;
  906. return false;
  907. }
  908. async _retryWithProgressIfNotConnected(progress, selector, strict, performActionPreChecks, action) {
  909. progress.log(`waiting for ${this._asLocator(selector)}`);
  910. return this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async (continuePolling) => {
  911. if (performActionPreChecks)
  912. await this._page.performActionPreChecks(progress);
  913. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, { strict }));
  914. if (!resolved)
  915. return continuePolling;
  916. const result = await progress.race(resolved.injected.evaluateHandle((injected, { info, callId }) => {
  917. const elements = injected.querySelectorAll(info.parsed, document);
  918. if (callId)
  919. injected.markTargetElements(new Set(elements), callId);
  920. const element2 = elements[0];
  921. let log2 = "";
  922. if (elements.length > 1) {
  923. if (info.strict)
  924. throw injected.strictModeViolationError(info.parsed, elements);
  925. log2 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`;
  926. } else if (element2) {
  927. log2 = ` locator resolved to ${injected.previewNode(element2)}`;
  928. }
  929. return { log: log2, success: !!element2, element: element2 };
  930. }, { info: resolved.info, callId: progress.metadata.id }));
  931. const { log, success } = await progress.race(result.evaluate((r) => ({ log: r.log, success: r.success })));
  932. if (log)
  933. progress.log(log);
  934. if (!success) {
  935. result.dispose();
  936. return continuePolling;
  937. }
  938. const element = await progress.race(result.evaluateHandle((r) => r.element));
  939. result.dispose();
  940. try {
  941. const result2 = await action(element);
  942. if (result2 === "error:notconnected") {
  943. progress.log("element was detached from the DOM, retrying");
  944. return continuePolling;
  945. }
  946. return result2;
  947. } finally {
  948. element?.dispose();
  949. }
  950. });
  951. }
  952. async rafrafTimeoutScreenshotElementWithProgress(progress, selector, timeout, options) {
  953. return await this._retryWithProgressIfNotConnected(progress, selector, true, true, async (handle) => {
  954. await handle._frame.rafrafTimeout(progress, timeout);
  955. return await this._page.screenshotter.screenshotElement(progress, handle, options);
  956. });
  957. }
  958. async click(progress, selector, options) {
  959. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._click(progress, { ...options, waitAfter: !options.noWaitAfter })));
  960. }
  961. async dblclick(progress, selector, options) {
  962. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._dblclick(progress, options)));
  963. }
  964. async dragAndDrop(progress, source, target, options) {
  965. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, source, options.strict, !options.force, async (handle) => {
  966. return handle._retryPointerAction(progress, "move and down", false, async (point) => {
  967. await this._page.mouse.move(progress, point.x, point.y);
  968. await this._page.mouse.down(progress);
  969. }, {
  970. ...options,
  971. waitAfter: "disabled",
  972. position: options.sourcePosition
  973. });
  974. }));
  975. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, target, options.strict, false, async (handle) => {
  976. return handle._retryPointerAction(progress, "move and up", false, async (point) => {
  977. await this._page.mouse.move(progress, point.x, point.y, { steps: options.steps });
  978. await this._page.mouse.up(progress);
  979. }, {
  980. ...options,
  981. waitAfter: "disabled",
  982. position: options.targetPosition
  983. });
  984. }));
  985. }
  986. async tap(progress, selector, options) {
  987. if (!this._page.browserContext._options.hasTouch)
  988. throw new Error("The page does not support tap. Use hasTouch context option to enable touch support.");
  989. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._tap(progress, options)));
  990. }
  991. async fill(progress, selector, value, options) {
  992. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._fill(progress, value, options)));
  993. }
  994. async focus(progress, selector, options) {
  995. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true, (handle) => handle._focus(progress)));
  996. }
  997. async blur(progress, selector, options) {
  998. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true, (handle) => handle._blur(progress)));
  999. }
  1000. async resolveSelector(progress, selector, options = {}) {
  1001. const element = await progress.race(this.selectors.query(selector, options));
  1002. if (!element)
  1003. throw new Error(`No element matching ${selector}`);
  1004. const generated = await progress.race(element.evaluateInUtility(async ([injected, node]) => {
  1005. return injected.generateSelectorSimple(node);
  1006. }, {}));
  1007. if (!generated)
  1008. throw new Error(`Unable to generate locator for ${selector}`);
  1009. let frame = element._frame;
  1010. const result = [generated];
  1011. while (frame?.parentFrame()) {
  1012. const frameElement = await progress.race(frame.frameElement());
  1013. if (frameElement) {
  1014. const generated2 = await progress.race(frameElement.evaluateInUtility(async ([injected, node]) => {
  1015. return injected.generateSelectorSimple(node);
  1016. }, {}));
  1017. frameElement.dispose();
  1018. if (generated2 === "error:notconnected" || !generated2)
  1019. throw new Error(`Unable to generate locator for ${selector}`);
  1020. result.push(generated2);
  1021. }
  1022. frame = frame.parentFrame();
  1023. }
  1024. const resolvedSelector = result.reverse().join(" >> internal:control=enter-frame >> ");
  1025. return { resolvedSelector };
  1026. }
  1027. async textContent(progress, selector, options, scope) {
  1028. return this._callOnElementOnceMatches(progress, selector, (injected, element) => element.textContent, void 0, options, scope);
  1029. }
  1030. async innerText(progress, selector, options, scope) {
  1031. return this._callOnElementOnceMatches(progress, selector, (injectedScript, element) => {
  1032. if (element.namespaceURI !== "http://www.w3.org/1999/xhtml")
  1033. throw injectedScript.createStacklessError("Node is not an HTMLElement");
  1034. return element.innerText;
  1035. }, void 0, options, scope);
  1036. }
  1037. async innerHTML(progress, selector, options, scope) {
  1038. return this._callOnElementOnceMatches(progress, selector, (injected, element) => element.innerHTML, void 0, options, scope);
  1039. }
  1040. async getAttribute(progress, selector, name, options, scope) {
  1041. return this._callOnElementOnceMatches(progress, selector, (injected, element, data) => element.getAttribute(data.name), { name }, options, scope);
  1042. }
  1043. async inputValue(progress, selector, options, scope) {
  1044. return this._callOnElementOnceMatches(progress, selector, (injectedScript, node) => {
  1045. const element = injectedScript.retarget(node, "follow-label");
  1046. if (!element || element.nodeName !== "INPUT" && element.nodeName !== "TEXTAREA" && element.nodeName !== "SELECT")
  1047. throw injectedScript.createStacklessError("Node is not an <input>, <textarea> or <select> element");
  1048. return element.value;
  1049. }, void 0, options, scope);
  1050. }
  1051. async highlight(progress, selector) {
  1052. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector));
  1053. if (!resolved)
  1054. return;
  1055. return await progress.race(resolved.injected.evaluate((injected, { info }) => {
  1056. return injected.highlight(info.parsed);
  1057. }, { info: resolved.info }));
  1058. }
  1059. async hideHighlight() {
  1060. return this.raceAgainstEvaluationStallingEvents(async () => {
  1061. const context = await this._utilityContext();
  1062. const injectedScript = await context.injectedScript();
  1063. return await injectedScript.evaluate((injected) => {
  1064. return injected.hideHighlight();
  1065. });
  1066. });
  1067. }
  1068. async _elementState(progress, selector, state, options, scope) {
  1069. const result = await this._callOnElementOnceMatches(progress, selector, (injected, element, data) => {
  1070. return injected.elementState(element, data.state);
  1071. }, { state }, options, scope);
  1072. if (result.received === "error:notconnected")
  1073. dom.throwElementIsNotAttached();
  1074. return result.matches;
  1075. }
  1076. async isVisible(progress, selector, options = {}, scope) {
  1077. progress.log(` checking visibility of ${this._asLocator(selector)}`);
  1078. return await this.isVisibleInternal(progress, selector, options, scope);
  1079. }
  1080. async isVisibleInternal(progress, selector, options = {}, scope) {
  1081. try {
  1082. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, options, scope));
  1083. if (!resolved)
  1084. return false;
  1085. return await progress.race(resolved.injected.evaluate((injected, { info, root }) => {
  1086. const element = injected.querySelector(info.parsed, root || document, info.strict);
  1087. const state = element ? injected.elementState(element, "visible") : { matches: false, received: "error:notconnected" };
  1088. return state.matches;
  1089. }, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
  1090. } catch (e) {
  1091. if (this.isNonRetriableError(e))
  1092. throw e;
  1093. return false;
  1094. }
  1095. }
  1096. async isHidden(progress, selector, options = {}, scope) {
  1097. return !await this.isVisible(progress, selector, options, scope);
  1098. }
  1099. async isDisabled(progress, selector, options, scope) {
  1100. return this._elementState(progress, selector, "disabled", options, scope);
  1101. }
  1102. async isEnabled(progress, selector, options, scope) {
  1103. return this._elementState(progress, selector, "enabled", options, scope);
  1104. }
  1105. async isEditable(progress, selector, options, scope) {
  1106. return this._elementState(progress, selector, "editable", options, scope);
  1107. }
  1108. async isChecked(progress, selector, options, scope) {
  1109. return this._elementState(progress, selector, "checked", options, scope);
  1110. }
  1111. async hover(progress, selector, options) {
  1112. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._hover(progress, options)));
  1113. }
  1114. async selectOption(progress, selector, elements, values, options) {
  1115. return await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._selectOption(progress, elements, values, options));
  1116. }
  1117. async setInputFiles(progress, selector, params) {
  1118. const inputFileItems = await (0, import_fileUploadUtils.prepareFilesForUpload)(this, params);
  1119. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, params.strict, true, (handle) => handle._setInputFiles(progress, inputFileItems)));
  1120. }
  1121. async type(progress, selector, text, options) {
  1122. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true, (handle) => handle._type(progress, text, options)));
  1123. }
  1124. async press(progress, selector, key, options) {
  1125. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true, (handle) => handle._press(progress, key, options)));
  1126. }
  1127. async check(progress, selector, options) {
  1128. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._setChecked(progress, true, options)));
  1129. }
  1130. async uncheck(progress, selector, options) {
  1131. return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force, (handle) => handle._setChecked(progress, false, options)));
  1132. }
  1133. async waitForTimeout(progress, timeout) {
  1134. return progress.wait(timeout);
  1135. }
  1136. async ariaSnapshot(progress, selector) {
  1137. return await this._retryWithProgressIfNotConnected(progress, selector, true, true, (handle) => progress.race(handle.ariaSnapshot()));
  1138. }
  1139. async expect(progress, selector, options, timeout) {
  1140. progress.log(`${(0, import_utils.renderTitleForCall)(progress.metadata)}${timeout ? ` with timeout ${timeout}ms` : ""}`);
  1141. const lastIntermediateResult = { isSet: false };
  1142. const fixupMetadataError = (result) => {
  1143. if (result.matches === options.isNot)
  1144. progress.metadata.error = { error: { name: "Expect", message: "Expect failed" } };
  1145. };
  1146. try {
  1147. if (selector)
  1148. progress.log(`waiting for ${this._asLocator(selector)}`);
  1149. await this._page.performActionPreChecks(progress);
  1150. try {
  1151. const resultOneShot = await this._expectInternal(progress, selector, options, lastIntermediateResult, true);
  1152. if (resultOneShot.matches !== options.isNot)
  1153. return resultOneShot;
  1154. } catch (e) {
  1155. if (this.isNonRetriableError(e))
  1156. throw e;
  1157. }
  1158. const result = await this.retryWithProgressAndTimeouts(progress, [100, 250, 500, 1e3], async (continuePolling) => {
  1159. await this._page.performActionPreChecks(progress);
  1160. const { matches, received } = await this._expectInternal(progress, selector, options, lastIntermediateResult, false);
  1161. if (matches === options.isNot) {
  1162. return continuePolling;
  1163. }
  1164. return { matches, received };
  1165. });
  1166. fixupMetadataError(result);
  1167. return result;
  1168. } catch (e) {
  1169. const result = { matches: options.isNot, log: (0, import_callLog.compressCallLog)(progress.metadata.log) };
  1170. if ((0, import_selectorParser.isInvalidSelectorError)(e)) {
  1171. result.errorMessage = "Error: " + e.message;
  1172. } else if (js.isJavaScriptErrorInEvaluate(e)) {
  1173. result.errorMessage = e.message;
  1174. } else if (lastIntermediateResult.isSet) {
  1175. result.received = lastIntermediateResult.received;
  1176. result.errorMessage = lastIntermediateResult.errorMessage;
  1177. }
  1178. if (e instanceof import_errors.TimeoutError)
  1179. result.timedOut = true;
  1180. fixupMetadataError(result);
  1181. return result;
  1182. }
  1183. }
  1184. async _expectInternal(progress, selector, options, lastIntermediateResult, noAbort) {
  1185. const race = (p) => noAbort ? p : progress.race(p);
  1186. const selectorInFrame = selector ? await race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0;
  1187. const { frame, info } = selectorInFrame || { frame: this, info: void 0 };
  1188. const world = options.expression === "to.have.property" ? "main" : info?.world ?? "utility";
  1189. const context = await race(frame._context(world));
  1190. const injected = await race(context.injectedScript());
  1191. const { log, matches, received, missingReceived } = await race(injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
  1192. const elements = info2 ? injected2.querySelectorAll(info2.parsed, document) : [];
  1193. if (callId)
  1194. injected2.markTargetElements(new Set(elements), callId);
  1195. const isArray = options2.expression === "to.have.count" || options2.expression.endsWith(".array");
  1196. let log2 = "";
  1197. if (isArray)
  1198. log2 = ` locator resolved to ${elements.length} element${elements.length === 1 ? "" : "s"}`;
  1199. else if (elements.length > 1)
  1200. throw injected2.strictModeViolationError(info2.parsed, elements);
  1201. else if (elements.length)
  1202. log2 = ` locator resolved to ${injected2.previewNode(elements[0])}`;
  1203. return { log: log2, ...await injected2.expect(elements[0], options2, elements) };
  1204. }, { info, options, callId: progress.metadata.id }));
  1205. if (log)
  1206. progress.log(log);
  1207. if (matches === options.isNot) {
  1208. if (missingReceived) {
  1209. lastIntermediateResult.errorMessage = "Error: element(s) not found";
  1210. } else {
  1211. lastIntermediateResult.errorMessage = void 0;
  1212. lastIntermediateResult.received = received;
  1213. }
  1214. lastIntermediateResult.isSet = true;
  1215. if (!missingReceived && !Array.isArray(received))
  1216. progress.log(` unexpected value "${renderUnexpectedValue(options.expression, received)}"`);
  1217. }
  1218. return { matches, received };
  1219. }
  1220. async waitForFunctionExpression(progress, expression, isFunction, arg, options, world = "main") {
  1221. if (typeof options.pollingInterval === "number")
  1222. (0, import_utils.assert)(options.pollingInterval > 0, "Cannot poll with non-positive interval: " + options.pollingInterval);
  1223. expression = js.normalizeEvaluationExpression(expression, isFunction);
  1224. return this.retryWithProgressAndTimeouts(progress, [100], async () => {
  1225. const context = world === "main" ? await progress.race(this._mainContext()) : await progress.race(this._utilityContext());
  1226. const injectedScript = await progress.race(context.injectedScript());
  1227. const handle = await progress.race(injectedScript.evaluateHandle((injected, { expression: expression2, isFunction: isFunction2, polling, arg: arg2 }) => {
  1228. let evaledExpression;
  1229. const predicate = () => {
  1230. let result2 = evaledExpression ?? globalThis.eval(expression2);
  1231. if (isFunction2 === true) {
  1232. evaledExpression = result2;
  1233. result2 = result2(arg2);
  1234. } else if (isFunction2 === false) {
  1235. result2 = result2;
  1236. } else {
  1237. if (typeof result2 === "function") {
  1238. evaledExpression = result2;
  1239. result2 = result2(arg2);
  1240. }
  1241. }
  1242. return result2;
  1243. };
  1244. let fulfill;
  1245. let reject;
  1246. let aborted = false;
  1247. const result = new Promise((f, r) => {
  1248. fulfill = f;
  1249. reject = r;
  1250. });
  1251. const next = () => {
  1252. if (aborted)
  1253. return;
  1254. try {
  1255. const success = predicate();
  1256. if (success) {
  1257. fulfill(success);
  1258. return;
  1259. }
  1260. if (typeof polling !== "number")
  1261. injected.utils.builtins.requestAnimationFrame(next);
  1262. else
  1263. injected.utils.builtins.setTimeout(next, polling);
  1264. } catch (e) {
  1265. reject(e);
  1266. }
  1267. };
  1268. next();
  1269. return { result, abort: () => aborted = true };
  1270. }, { expression, isFunction, polling: options.pollingInterval, arg }));
  1271. try {
  1272. return await progress.race(handle.evaluateHandle((h) => h.result));
  1273. } catch (error) {
  1274. await handle.evaluate((h) => h.abort()).catch(() => {
  1275. });
  1276. throw error;
  1277. } finally {
  1278. handle.dispose();
  1279. }
  1280. });
  1281. }
  1282. async waitForFunctionValueInUtility(progress, pageFunction) {
  1283. const expression = `() => {
  1284. const result = (${pageFunction})();
  1285. if (!result)
  1286. return result;
  1287. return JSON.stringify(result);
  1288. }`;
  1289. const handle = await this.waitForFunctionExpression(progress, expression, true, void 0, {}, "utility");
  1290. return JSON.parse(handle.rawValue());
  1291. }
  1292. async title() {
  1293. const context = await this._utilityContext();
  1294. return context.evaluate(() => document.title);
  1295. }
  1296. async rafrafTimeout(progress, timeout) {
  1297. if (timeout === 0)
  1298. return;
  1299. const context = await progress.race(this._utilityContext());
  1300. await Promise.all([
  1301. // wait for double raf
  1302. progress.race(context.evaluate(() => new Promise((x) => {
  1303. requestAnimationFrame(() => {
  1304. requestAnimationFrame(x);
  1305. });
  1306. }))),
  1307. progress.wait(timeout)
  1308. ]);
  1309. }
  1310. _onDetached() {
  1311. this._stopNetworkIdleTimer();
  1312. this._detachedScope.close(new Error("Frame was detached"));
  1313. for (const data of this._contextData.values()) {
  1314. if (data.context)
  1315. data.context.contextDestroyed("Frame was detached");
  1316. data.contextPromise.resolve({ destroyedReason: "Frame was detached" });
  1317. }
  1318. if (this._parentFrame)
  1319. this._parentFrame._childFrames.delete(this);
  1320. this._parentFrame = null;
  1321. }
  1322. async _callOnElementOnceMatches(progress, selector, body, taskData, options, scope) {
  1323. const callbackText = body.toString();
  1324. progress.log(`waiting for ${this._asLocator(selector)}`);
  1325. const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async (continuePolling) => {
  1326. const resolved = await progress.race(this.selectors.resolveInjectedForSelector(selector, options, scope));
  1327. if (!resolved)
  1328. return continuePolling;
  1329. const { log, success, value } = await progress.race(resolved.injected.evaluate((injected, { info, callbackText: callbackText2, taskData: taskData2, callId, root }) => {
  1330. const callback = injected.eval(callbackText2);
  1331. const element = injected.querySelector(info.parsed, root || document, info.strict);
  1332. if (!element)
  1333. return { success: false };
  1334. const log2 = ` locator resolved to ${injected.previewNode(element)}`;
  1335. if (callId)
  1336. injected.markTargetElements(/* @__PURE__ */ new Set([element]), callId);
  1337. return { log: log2, success: true, value: callback(injected, element, taskData2) };
  1338. }, { info: resolved.info, callbackText, taskData, callId: progress.metadata.id, root: resolved.frame === this ? scope : void 0 }));
  1339. if (log)
  1340. progress.log(log);
  1341. if (!success)
  1342. return continuePolling;
  1343. return value;
  1344. });
  1345. return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise;
  1346. }
  1347. _setContext(world, context) {
  1348. const data = this._contextData.get(world);
  1349. data.context = context;
  1350. if (context)
  1351. data.contextPromise.resolve(context);
  1352. else
  1353. data.contextPromise = new import_manualPromise.ManualPromise();
  1354. }
  1355. _contextCreated(world, context) {
  1356. const data = this._contextData.get(world);
  1357. if (data.context) {
  1358. data.context.contextDestroyed("Execution context was destroyed, most likely because of a navigation");
  1359. this._setContext(world, null);
  1360. }
  1361. this._setContext(world, context);
  1362. }
  1363. _contextDestroyed(context) {
  1364. if (this._detachedScope.isClosed())
  1365. return;
  1366. context.contextDestroyed("Execution context was destroyed, most likely because of a navigation");
  1367. for (const [world, data] of this._contextData) {
  1368. if (data.context === context)
  1369. this._setContext(world, null);
  1370. }
  1371. }
  1372. _startNetworkIdleTimer() {
  1373. (0, import_utils.assert)(!this._networkIdleTimer);
  1374. if (this._firedLifecycleEvents.has("networkidle") || this._detachedScope.isClosed())
  1375. return;
  1376. this._networkIdleTimer = setTimeout(() => {
  1377. this._firedNetworkIdleSelf = true;
  1378. this._page.mainFrame()._recalculateNetworkIdle();
  1379. }, 500);
  1380. }
  1381. _stopNetworkIdleTimer() {
  1382. if (this._networkIdleTimer)
  1383. clearTimeout(this._networkIdleTimer);
  1384. this._networkIdleTimer = void 0;
  1385. this._firedNetworkIdleSelf = false;
  1386. }
  1387. async extendInjectedScript(source, arg) {
  1388. const context = await this._context("main");
  1389. const injectedScriptHandle = await context.injectedScript();
  1390. await injectedScriptHandle.evaluate((injectedScript, { source: source2, arg: arg2 }) => {
  1391. injectedScript.extend(source2, arg2);
  1392. }, { source, arg });
  1393. }
  1394. _asLocator(selector) {
  1395. return (0, import_utils.asLocator)(this._page.browserContext._browser.sdkLanguage(), selector);
  1396. }
  1397. }
  1398. class SignalBarrier {
  1399. constructor(progress) {
  1400. this._protectCount = 0;
  1401. this._promise = new import_manualPromise.ManualPromise();
  1402. this._progress = progress;
  1403. this.retain();
  1404. }
  1405. waitFor() {
  1406. this.release();
  1407. return this._progress.race(this._promise);
  1408. }
  1409. addFrameNavigation(frame) {
  1410. if (frame.parentFrame())
  1411. return;
  1412. this.retain();
  1413. const waiter = import_helper.helper.waitForEvent(this._progress, frame, Frame.Events.InternalNavigation, (e) => {
  1414. if (!e.isPublic)
  1415. return false;
  1416. if (!e.error && this._progress)
  1417. this._progress.log(` navigated to "${frame._url}"`);
  1418. return true;
  1419. });
  1420. import_utils.LongStandingScope.raceMultiple([
  1421. frame._page.openScope,
  1422. frame._detachedScope
  1423. ], waiter.promise).catch(() => {
  1424. }).finally(() => {
  1425. waiter.dispose();
  1426. this.release();
  1427. });
  1428. }
  1429. retain() {
  1430. ++this._protectCount;
  1431. }
  1432. release() {
  1433. --this._protectCount;
  1434. if (!this._protectCount)
  1435. this._promise.resolve();
  1436. }
  1437. }
  1438. function verifyLifecycle(name, waitUntil) {
  1439. if (waitUntil === "networkidle0")
  1440. waitUntil = "networkidle";
  1441. if (!types.kLifecycleEvents.has(waitUntil))
  1442. throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
  1443. return waitUntil;
  1444. }
  1445. function renderUnexpectedValue(expression, received) {
  1446. if (expression === "to.match.aria")
  1447. return received ? received.raw : received;
  1448. return received;
  1449. }
  1450. // Annotate the CommonJS export names for ESM import in node:
  1451. 0 && (module.exports = {
  1452. Frame,
  1453. FrameManager,
  1454. NavigationAbortedError
  1455. });