FrameManager.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. /**
  2. * Copyright 2017 Google Inc. All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. const EventEmitter = require('events');
  17. const {helper, assert, debugError} = require('./helper');
  18. const {Events} = require('./Events');
  19. const {ExecutionContext, EVALUATION_SCRIPT_URL} = require('./ExecutionContext');
  20. const {LifecycleWatcher} = require('./LifecycleWatcher');
  21. const {DOMWorld} = require('./DOMWorld');
  22. const {NetworkManager} = require('./NetworkManager');
  23. const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
  24. class FrameManager extends EventEmitter {
  25. /**
  26. * @param {!Puppeteer.CDPSession} client
  27. * @param {!Puppeteer.Page} page
  28. * @param {boolean} ignoreHTTPSErrors
  29. * @param {!Puppeteer.TimeoutSettings} timeoutSettings
  30. */
  31. constructor(client, page, ignoreHTTPSErrors, timeoutSettings) {
  32. super();
  33. this._client = client;
  34. this._page = page;
  35. this._networkManager = new NetworkManager(client, ignoreHTTPSErrors, this);
  36. this._timeoutSettings = timeoutSettings;
  37. /** @type {!Map<string, !Frame>} */
  38. this._frames = new Map();
  39. /** @type {!Map<number, !ExecutionContext>} */
  40. this._contextIdToContext = new Map();
  41. /** @type {!Set<string>} */
  42. this._isolatedWorlds = new Set();
  43. this._client.on('Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId));
  44. this._client.on('Page.frameNavigated', event => this._onFrameNavigated(event.frame));
  45. this._client.on('Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url));
  46. this._client.on('Page.frameDetached', event => this._onFrameDetached(event.frameId));
  47. this._client.on('Page.frameStoppedLoading', event => this._onFrameStoppedLoading(event.frameId));
  48. this._client.on('Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context));
  49. this._client.on('Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId));
  50. this._client.on('Runtime.executionContextsCleared', event => this._onExecutionContextsCleared());
  51. this._client.on('Page.lifecycleEvent', event => this._onLifecycleEvent(event));
  52. }
  53. async initialize() {
  54. const [,{frameTree}] = await Promise.all([
  55. this._client.send('Page.enable'),
  56. this._client.send('Page.getFrameTree'),
  57. ]);
  58. this._handleFrameTree(frameTree);
  59. await Promise.all([
  60. this._client.send('Page.setLifecycleEventsEnabled', { enabled: true }),
  61. this._client.send('Runtime.enable', {}).then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)),
  62. this._networkManager.initialize(),
  63. ]);
  64. }
  65. /**
  66. * @return {!NetworkManager}
  67. */
  68. networkManager() {
  69. return this._networkManager;
  70. }
  71. /**
  72. * @param {!Puppeteer.Frame} frame
  73. * @param {string} url
  74. * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array<string>}=} options
  75. * @return {!Promise<?Puppeteer.Response>}
  76. */
  77. async navigateFrame(frame, url, options = {}) {
  78. assertNoLegacyNavigationOptions(options);
  79. const {
  80. referer = this._networkManager.extraHTTPHeaders()['referer'],
  81. waitUntil = ['load'],
  82. timeout = this._timeoutSettings.navigationTimeout(),
  83. } = options;
  84. const watcher = new LifecycleWatcher(this, frame, waitUntil, timeout);
  85. let ensureNewDocumentNavigation = false;
  86. let error = await Promise.race([
  87. navigate(this._client, url, referer, frame._id),
  88. watcher.timeoutOrTerminationPromise(),
  89. ]);
  90. if (!error) {
  91. error = await Promise.race([
  92. watcher.timeoutOrTerminationPromise(),
  93. ensureNewDocumentNavigation ? watcher.newDocumentNavigationPromise() : watcher.sameDocumentNavigationPromise(),
  94. ]);
  95. }
  96. watcher.dispose();
  97. if (error)
  98. throw error;
  99. return watcher.navigationResponse();
  100. /**
  101. * @param {!Puppeteer.CDPSession} client
  102. * @param {string} url
  103. * @param {string} referrer
  104. * @param {string} frameId
  105. * @return {!Promise<?Error>}
  106. */
  107. async function navigate(client, url, referrer, frameId) {
  108. try {
  109. const response = await client.send('Page.navigate', {url, referrer, frameId});
  110. ensureNewDocumentNavigation = !!response.loaderId;
  111. return response.errorText ? new Error(`${response.errorText} at ${url}`) : null;
  112. } catch (error) {
  113. return error;
  114. }
  115. }
  116. }
  117. /**
  118. * @param {!Puppeteer.Frame} frame
  119. * @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
  120. * @return {!Promise<?Puppeteer.Response>}
  121. */
  122. async waitForFrameNavigation(frame, options = {}) {
  123. assertNoLegacyNavigationOptions(options);
  124. const {
  125. waitUntil = ['load'],
  126. timeout = this._timeoutSettings.navigationTimeout(),
  127. } = options;
  128. const watcher = new LifecycleWatcher(this, frame, waitUntil, timeout);
  129. const error = await Promise.race([
  130. watcher.timeoutOrTerminationPromise(),
  131. watcher.sameDocumentNavigationPromise(),
  132. watcher.newDocumentNavigationPromise()
  133. ]);
  134. watcher.dispose();
  135. if (error)
  136. throw error;
  137. return watcher.navigationResponse();
  138. }
  139. /**
  140. * @param {!Protocol.Page.lifecycleEventPayload} event
  141. */
  142. _onLifecycleEvent(event) {
  143. const frame = this._frames.get(event.frameId);
  144. if (!frame)
  145. return;
  146. frame._onLifecycleEvent(event.loaderId, event.name);
  147. this.emit(Events.FrameManager.LifecycleEvent, frame);
  148. }
  149. /**
  150. * @param {string} frameId
  151. */
  152. _onFrameStoppedLoading(frameId) {
  153. const frame = this._frames.get(frameId);
  154. if (!frame)
  155. return;
  156. frame._onLoadingStopped();
  157. this.emit(Events.FrameManager.LifecycleEvent, frame);
  158. }
  159. /**
  160. * @param {!Protocol.Page.FrameTree} frameTree
  161. */
  162. _handleFrameTree(frameTree) {
  163. if (frameTree.frame.parentId)
  164. this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId);
  165. this._onFrameNavigated(frameTree.frame);
  166. if (!frameTree.childFrames)
  167. return;
  168. for (const child of frameTree.childFrames)
  169. this._handleFrameTree(child);
  170. }
  171. /**
  172. * @return {!Puppeteer.Page}
  173. */
  174. page() {
  175. return this._page;
  176. }
  177. /**
  178. * @return {!Frame}
  179. */
  180. mainFrame() {
  181. return this._mainFrame;
  182. }
  183. /**
  184. * @return {!Array<!Frame>}
  185. */
  186. frames() {
  187. return Array.from(this._frames.values());
  188. }
  189. /**
  190. * @param {!string} frameId
  191. * @return {?Frame}
  192. */
  193. frame(frameId) {
  194. return this._frames.get(frameId) || null;
  195. }
  196. /**
  197. * @param {string} frameId
  198. * @param {?string} parentFrameId
  199. */
  200. _onFrameAttached(frameId, parentFrameId) {
  201. if (this._frames.has(frameId))
  202. return;
  203. assert(parentFrameId);
  204. const parentFrame = this._frames.get(parentFrameId);
  205. const frame = new Frame(this, this._client, parentFrame, frameId);
  206. this._frames.set(frame._id, frame);
  207. this.emit(Events.FrameManager.FrameAttached, frame);
  208. }
  209. /**
  210. * @param {!Protocol.Page.Frame} framePayload
  211. */
  212. _onFrameNavigated(framePayload) {
  213. const isMainFrame = !framePayload.parentId;
  214. let frame = isMainFrame ? this._mainFrame : this._frames.get(framePayload.id);
  215. assert(isMainFrame || frame, 'We either navigate top level or have old version of the navigated frame');
  216. // Detach all child frames first.
  217. if (frame) {
  218. for (const child of frame.childFrames())
  219. this._removeFramesRecursively(child);
  220. }
  221. // Update or create main frame.
  222. if (isMainFrame) {
  223. if (frame) {
  224. // Update frame id to retain frame identity on cross-process navigation.
  225. this._frames.delete(frame._id);
  226. frame._id = framePayload.id;
  227. } else {
  228. // Initial main frame navigation.
  229. frame = new Frame(this, this._client, null, framePayload.id);
  230. }
  231. this._frames.set(framePayload.id, frame);
  232. this._mainFrame = frame;
  233. }
  234. // Update frame payload.
  235. frame._navigated(framePayload);
  236. this.emit(Events.FrameManager.FrameNavigated, frame);
  237. }
  238. /**
  239. * @param {string} name
  240. */
  241. async _ensureIsolatedWorld(name) {
  242. if (this._isolatedWorlds.has(name))
  243. return;
  244. this._isolatedWorlds.add(name);
  245. await this._client.send('Page.addScriptToEvaluateOnNewDocument', {
  246. source: `//# sourceURL=${EVALUATION_SCRIPT_URL}`,
  247. worldName: name,
  248. }),
  249. await Promise.all(this.frames().map(frame => this._client.send('Page.createIsolatedWorld', {
  250. frameId: frame._id,
  251. grantUniveralAccess: true,
  252. worldName: name,
  253. }).catch(debugError))); // frames might be removed before we send this
  254. }
  255. /**
  256. * @param {string} frameId
  257. * @param {string} url
  258. */
  259. _onFrameNavigatedWithinDocument(frameId, url) {
  260. const frame = this._frames.get(frameId);
  261. if (!frame)
  262. return;
  263. frame._navigatedWithinDocument(url);
  264. this.emit(Events.FrameManager.FrameNavigatedWithinDocument, frame);
  265. this.emit(Events.FrameManager.FrameNavigated, frame);
  266. }
  267. /**
  268. * @param {string} frameId
  269. */
  270. _onFrameDetached(frameId) {
  271. const frame = this._frames.get(frameId);
  272. if (frame)
  273. this._removeFramesRecursively(frame);
  274. }
  275. _onExecutionContextCreated(contextPayload) {
  276. const frameId = contextPayload.auxData ? contextPayload.auxData.frameId : null;
  277. const frame = this._frames.get(frameId) || null;
  278. let world = null;
  279. if (frame) {
  280. if (contextPayload.auxData && !!contextPayload.auxData['isDefault']) {
  281. world = frame._mainWorld;
  282. } else if (contextPayload.name === UTILITY_WORLD_NAME && !frame._secondaryWorld._hasContext()) {
  283. // In case of multiple sessions to the same target, there's a race between
  284. // connections so we might end up creating multiple isolated worlds.
  285. // We can use either.
  286. world = frame._secondaryWorld;
  287. }
  288. }
  289. if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated')
  290. this._isolatedWorlds.add(contextPayload.name);
  291. /** @type {!ExecutionContext} */
  292. const context = new ExecutionContext(this._client, contextPayload, world);
  293. if (world)
  294. world._setContext(context);
  295. this._contextIdToContext.set(contextPayload.id, context);
  296. }
  297. /**
  298. * @param {number} executionContextId
  299. */
  300. _onExecutionContextDestroyed(executionContextId) {
  301. const context = this._contextIdToContext.get(executionContextId);
  302. if (!context)
  303. return;
  304. this._contextIdToContext.delete(executionContextId);
  305. if (context._world)
  306. context._world._setContext(null);
  307. }
  308. _onExecutionContextsCleared() {
  309. for (const context of this._contextIdToContext.values()) {
  310. if (context._world)
  311. context._world._setContext(null);
  312. }
  313. this._contextIdToContext.clear();
  314. }
  315. /**
  316. * @param {number} contextId
  317. * @return {!ExecutionContext}
  318. */
  319. executionContextById(contextId) {
  320. const context = this._contextIdToContext.get(contextId);
  321. assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
  322. return context;
  323. }
  324. /**
  325. * @param {!Frame} frame
  326. */
  327. _removeFramesRecursively(frame) {
  328. for (const child of frame.childFrames())
  329. this._removeFramesRecursively(child);
  330. frame._detach();
  331. this._frames.delete(frame._id);
  332. this.emit(Events.FrameManager.FrameDetached, frame);
  333. }
  334. }
  335. /**
  336. * @unrestricted
  337. */
  338. class Frame {
  339. /**
  340. * @param {!FrameManager} frameManager
  341. * @param {!Puppeteer.CDPSession} client
  342. * @param {?Frame} parentFrame
  343. * @param {string} frameId
  344. */
  345. constructor(frameManager, client, parentFrame, frameId) {
  346. this._frameManager = frameManager;
  347. this._client = client;
  348. this._parentFrame = parentFrame;
  349. this._url = '';
  350. this._id = frameId;
  351. this._detached = false;
  352. this._loaderId = '';
  353. /** @type {!Set<string>} */
  354. this._lifecycleEvents = new Set();
  355. /** @type {!DOMWorld} */
  356. this._mainWorld = new DOMWorld(frameManager, this, frameManager._timeoutSettings);
  357. /** @type {!DOMWorld} */
  358. this._secondaryWorld = new DOMWorld(frameManager, this, frameManager._timeoutSettings);
  359. /** @type {!Set<!Frame>} */
  360. this._childFrames = new Set();
  361. if (this._parentFrame)
  362. this._parentFrame._childFrames.add(this);
  363. }
  364. /**
  365. * @param {string} url
  366. * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array<string>}=} options
  367. * @return {!Promise<?Puppeteer.Response>}
  368. */
  369. async goto(url, options) {
  370. return await this._frameManager.navigateFrame(this, url, options);
  371. }
  372. /**
  373. * @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
  374. * @return {!Promise<?Puppeteer.Response>}
  375. */
  376. async waitForNavigation(options) {
  377. return await this._frameManager.waitForFrameNavigation(this, options);
  378. }
  379. /**
  380. * @return {!Promise<!ExecutionContext>}
  381. */
  382. executionContext() {
  383. return this._mainWorld.executionContext();
  384. }
  385. /**
  386. * @param {Function|string} pageFunction
  387. * @param {!Array<*>} args
  388. * @return {!Promise<!Puppeteer.JSHandle>}
  389. */
  390. async evaluateHandle(pageFunction, ...args) {
  391. return this._mainWorld.evaluateHandle(pageFunction, ...args);
  392. }
  393. /**
  394. * @param {Function|string} pageFunction
  395. * @param {!Array<*>} args
  396. * @return {!Promise<*>}
  397. */
  398. async evaluate(pageFunction, ...args) {
  399. return this._mainWorld.evaluate(pageFunction, ...args);
  400. }
  401. /**
  402. * @param {string} selector
  403. * @return {!Promise<?Puppeteer.ElementHandle>}
  404. */
  405. async $(selector) {
  406. return this._mainWorld.$(selector);
  407. }
  408. /**
  409. * @param {string} expression
  410. * @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
  411. */
  412. async $x(expression) {
  413. return this._mainWorld.$x(expression);
  414. }
  415. /**
  416. * @param {string} selector
  417. * @param {Function|string} pageFunction
  418. * @param {!Array<*>} args
  419. * @return {!Promise<(!Object|undefined)>}
  420. */
  421. async $eval(selector, pageFunction, ...args) {
  422. return this._mainWorld.$eval(selector, pageFunction, ...args);
  423. }
  424. /**
  425. * @param {string} selector
  426. * @param {Function|string} pageFunction
  427. * @param {!Array<*>} args
  428. * @return {!Promise<(!Object|undefined)>}
  429. */
  430. async $$eval(selector, pageFunction, ...args) {
  431. return this._mainWorld.$$eval(selector, pageFunction, ...args);
  432. }
  433. /**
  434. * @param {string} selector
  435. * @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
  436. */
  437. async $$(selector) {
  438. return this._mainWorld.$$(selector);
  439. }
  440. /**
  441. * @return {!Promise<String>}
  442. */
  443. async content() {
  444. return this._secondaryWorld.content();
  445. }
  446. /**
  447. * @param {string} html
  448. * @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
  449. */
  450. async setContent(html, options = {}) {
  451. return this._secondaryWorld.setContent(html, options);
  452. }
  453. /**
  454. * @return {string}
  455. */
  456. name() {
  457. return this._name || '';
  458. }
  459. /**
  460. * @return {string}
  461. */
  462. url() {
  463. return this._url;
  464. }
  465. /**
  466. * @return {?Frame}
  467. */
  468. parentFrame() {
  469. return this._parentFrame;
  470. }
  471. /**
  472. * @return {!Array.<!Frame>}
  473. */
  474. childFrames() {
  475. return Array.from(this._childFrames);
  476. }
  477. /**
  478. * @return {boolean}
  479. */
  480. isDetached() {
  481. return this._detached;
  482. }
  483. /**
  484. * @param {!{url?: string, path?: string, content?: string, type?: string}} options
  485. * @return {!Promise<!Puppeteer.ElementHandle>}
  486. */
  487. async addScriptTag(options) {
  488. return this._mainWorld.addScriptTag(options);
  489. }
  490. /**
  491. * @param {!{url?: string, path?: string, content?: string}} options
  492. * @return {!Promise<!Puppeteer.ElementHandle>}
  493. */
  494. async addStyleTag(options) {
  495. return this._mainWorld.addStyleTag(options);
  496. }
  497. /**
  498. * @param {string} selector
  499. * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
  500. */
  501. async click(selector, options) {
  502. return this._secondaryWorld.click(selector, options);
  503. }
  504. /**
  505. * @param {string} selector
  506. */
  507. async focus(selector) {
  508. return this._secondaryWorld.focus(selector);
  509. }
  510. /**
  511. * @param {string} selector
  512. */
  513. async hover(selector) {
  514. return this._secondaryWorld.hover(selector);
  515. }
  516. /**
  517. * @param {string} selector
  518. * @param {!Array<string>} values
  519. * @return {!Promise<!Array<string>>}
  520. */
  521. select(selector, ...values){
  522. return this._secondaryWorld.select(selector, ...values);
  523. }
  524. /**
  525. * @param {string} selector
  526. */
  527. async tap(selector) {
  528. return this._secondaryWorld.tap(selector);
  529. }
  530. /**
  531. * @param {string} selector
  532. * @param {string} text
  533. * @param {{delay: (number|undefined)}=} options
  534. */
  535. async type(selector, text, options) {
  536. return this._mainWorld.type(selector, text, options);
  537. }
  538. /**
  539. * @param {(string|number|Function)} selectorOrFunctionOrTimeout
  540. * @param {!Object=} options
  541. * @param {!Array<*>} args
  542. * @return {!Promise<?Puppeteer.JSHandle>}
  543. */
  544. waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
  545. const xPathPattern = '//';
  546. if (helper.isString(selectorOrFunctionOrTimeout)) {
  547. const string = /** @type {string} */ (selectorOrFunctionOrTimeout);
  548. if (string.startsWith(xPathPattern))
  549. return this.waitForXPath(string, options);
  550. return this.waitForSelector(string, options);
  551. }
  552. if (helper.isNumber(selectorOrFunctionOrTimeout))
  553. return new Promise(fulfill => setTimeout(fulfill, /** @type {number} */ (selectorOrFunctionOrTimeout)));
  554. if (typeof selectorOrFunctionOrTimeout === 'function')
  555. return this.waitForFunction(selectorOrFunctionOrTimeout, options, ...args);
  556. return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
  557. }
  558. /**
  559. * @param {string} selector
  560. * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
  561. * @return {!Promise<?Puppeteer.ElementHandle>}
  562. */
  563. async waitForSelector(selector, options) {
  564. const handle = await this._secondaryWorld.waitForSelector(selector, options);
  565. if (!handle)
  566. return null;
  567. const mainExecutionContext = await this._mainWorld.executionContext();
  568. const result = await mainExecutionContext._adoptElementHandle(handle);
  569. await handle.dispose();
  570. return result;
  571. }
  572. /**
  573. * @param {string} xpath
  574. * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
  575. * @return {!Promise<?Puppeteer.ElementHandle>}
  576. */
  577. async waitForXPath(xpath, options) {
  578. const handle = await this._secondaryWorld.waitForXPath(xpath, options);
  579. if (!handle)
  580. return null;
  581. const mainExecutionContext = await this._mainWorld.executionContext();
  582. const result = await mainExecutionContext._adoptElementHandle(handle);
  583. await handle.dispose();
  584. return result;
  585. }
  586. /**
  587. * @param {Function|string} pageFunction
  588. * @param {!{polling?: string|number, timeout?: number}=} options
  589. * @return {!Promise<!Puppeteer.JSHandle>}
  590. */
  591. waitForFunction(pageFunction, options = {}, ...args) {
  592. return this._mainWorld.waitForFunction(pageFunction, options, ...args);
  593. }
  594. /**
  595. * @return {!Promise<string>}
  596. */
  597. async title() {
  598. return this._secondaryWorld.title();
  599. }
  600. /**
  601. * @param {!Protocol.Page.Frame} framePayload
  602. */
  603. _navigated(framePayload) {
  604. this._name = framePayload.name;
  605. // TODO(lushnikov): remove this once requestInterception has loaderId exposed.
  606. this._navigationURL = framePayload.url;
  607. this._url = framePayload.url;
  608. }
  609. /**
  610. * @param {string} url
  611. */
  612. _navigatedWithinDocument(url) {
  613. this._url = url;
  614. }
  615. /**
  616. * @param {string} loaderId
  617. * @param {string} name
  618. */
  619. _onLifecycleEvent(loaderId, name) {
  620. if (name === 'init') {
  621. this._loaderId = loaderId;
  622. this._lifecycleEvents.clear();
  623. }
  624. this._lifecycleEvents.add(name);
  625. }
  626. _onLoadingStopped() {
  627. this._lifecycleEvents.add('DOMContentLoaded');
  628. this._lifecycleEvents.add('load');
  629. }
  630. _detach() {
  631. this._detached = true;
  632. this._mainWorld._detach();
  633. this._secondaryWorld._detach();
  634. if (this._parentFrame)
  635. this._parentFrame._childFrames.delete(this);
  636. this._parentFrame = null;
  637. }
  638. }
  639. function assertNoLegacyNavigationOptions(options) {
  640. assert(options['networkIdleTimeout'] === undefined, 'ERROR: networkIdleTimeout option is no longer supported.');
  641. assert(options['networkIdleInflight'] === undefined, 'ERROR: networkIdleInflight option is no longer supported.');
  642. assert(options.waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead');
  643. }
  644. module.exports = {FrameManager, Frame};