@@ -219,36 +219,40 @@ public async Task<JSHandle> EvaluateFunctionHandleAsync(string function, params
219219 /// <param name="selector">A selector of an element to wait for</param>
220220 /// <param name="options">Optional waiting parameters</param>
221221 /// <returns>A task that resolves when element specified by selector string is added to DOM</returns>
222+ /// <seealso cref="WaitForXPathAsync(string, WaitForSelectorOptions)"/>
222223 /// <seealso cref="Page.WaitForSelectorAsync(string, WaitForSelectorOptions)"/>
223- public async Task < ElementHandle > WaitForSelectorAsync ( string selector , WaitForSelectorOptions options = null )
224- {
225- options = options ?? new WaitForSelectorOptions ( ) ;
226- const string predicate = @"
227- function predicate(selector, waitForVisible, waitForHidden) {
228- const node = document.querySelector(selector);
229- if (!node)
230- return waitForHidden;
231- if (!waitForVisible && !waitForHidden)
232- return node;
233- const style = window.getComputedStyle(node);
234- const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox();
235- const success = (waitForVisible === isVisible || waitForHidden === !isVisible);
236- return success ? node : null;
237-
238- function hasVisibleBoundingBox() {
239- const rect = node.getBoundingClientRect();
240- return !!(rect.top || rect.bottom || rect.width || rect.height);
241- }
242- }" ;
243- var polling = options . Visible || options . Hidden ? WaitForFunctionPollingOption . Raf : WaitForFunctionPollingOption . Mutation ;
244- var handle = await WaitForFunctionAsync ( predicate , new WaitForFunctionOptions
245- {
246- Timeout = options . Timeout ,
247- Polling = polling
248- } , selector , options . Visible , options . Hidden ) ;
249- return handle as ElementHandle ;
250- }
224+ public Task < ElementHandle > WaitForSelectorAsync ( string selector , WaitForSelectorOptions options = null )
225+ => WaitForSelectorOrXPathAsync ( selector , false , options ) ;
251226
227+ /// <summary>
228+ /// Waits for a selector to be added to the DOM
229+ /// </summary>
230+ /// <param name="xpath">A xpath selector of an element to wait for</param>
231+ /// <param name="options">Optional waiting parameters</param>
232+ /// <returns>A task that resolves when element specified by selector string is added to DOM</returns>
233+ /// <example>
234+ /// <code>
235+ /// <![CDATA[
236+ /// var browser = await Puppeteer.LaunchAsync(new LaunchOptions(), Downloader.DefaultRevision);
237+ /// var page = await browser.NewPageAsync();
238+ /// string currentURL = null;
239+ /// page.MainFrame
240+ /// .WaitForXPathAsync("//img")
241+ /// .ContinueWith(_ => Console.WriteLine("First URL with image: " + currentURL));
242+ /// foreach (var current in new[] { "https://example.com", "https://google.com", "https://bbc.com" })
243+ /// {
244+ /// currentURL = current;
245+ /// await page.GoToAsync(currentURL);
246+ /// }
247+ /// await browser.CloseAsync();
248+ /// ]]>
249+ /// </code>
250+ /// </example>
251+ /// <seealso cref="WaitForSelectorAsync(string, WaitForSelectorOptions)"/>
252+ /// <seealso cref="Page.WaitForXPathAsync(string, WaitForSelectorOptions)"/>
253+ public Task < ElementHandle > WaitForXPathAsync ( string xpath , WaitForSelectorOptions options = null )
254+ => WaitForSelectorOrXPathAsync ( xpath , true , options ) ;
255+
252256 /// <summary>
253257 /// Waits for a timeout
254258 /// </summary>
@@ -486,6 +490,39 @@ public Task SetContentAsync(string html)
486490 /// <seealso cref="Page.GetTitleAsync"/>
487491 public Task < string > GetTitleAsync ( ) => EvaluateExpressionAsync < string > ( "document.title" ) ;
488492
493+ internal async Task < ElementHandle > WaitForSelectorOrXPathAsync ( string selectorOrXPath , bool isXPath , WaitForSelectorOptions options = null )
494+ {
495+ options = options ?? new WaitForSelectorOptions ( ) ;
496+ const string predicate = @"
497+ function predicate(selectorOrXPath, isXPath, waitForVisible, waitForHidden) {
498+ const node = isXPath
499+ ? document.evaluate(selectorOrXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
500+ : document.querySelector(selectorOrXPath);
501+ if (!node)
502+ return waitForHidden;
503+ if (!waitForVisible && !waitForHidden)
504+ return node;
505+ const element = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
506+
507+ const style = window.getComputedStyle(element);
508+ const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox();
509+ const success = (waitForVisible === isVisible || waitForHidden === !isVisible);
510+ return success ? node : null;
511+
512+ function hasVisibleBoundingBox() {
513+ const rect = element.getBoundingClientRect();
514+ return !!(rect.top || rect.bottom || rect.width || rect.height);
515+ }
516+ }" ;
517+ var polling = options . Visible || options . Hidden ? WaitForFunctionPollingOption . Raf : WaitForFunctionPollingOption . Mutation ;
518+ var handle = await WaitForFunctionAsync ( predicate , new WaitForFunctionOptions
519+ {
520+ Timeout = options . Timeout ,
521+ Polling = polling
522+ } , selectorOrXPath , isXPath , options . Visible , options . Hidden ) ;
523+ return handle as ElementHandle ;
524+ }
525+
489526 internal void OnLifecycleEvent ( string loaderId , string name )
490527 {
491528 if ( name == "init" )
@@ -524,7 +561,7 @@ internal void Detach()
524561 {
525562 while ( WaitTasks . Count > 0 )
526563 {
527- WaitTasks [ 0 ] . Termiante ( new Exception ( "waitForSelector failed: frame got detached." ) ) ;
564+ WaitTasks [ 0 ] . Termiante ( new Exception ( "waitForFunction failed: frame got detached." ) ) ;
528565 }
529566 Detached = true ;
530567 if ( ParentFrame != null )
0 commit comments