/// <summary> /// Executes a function in browser context /// </summary> /// <typeparam name="T">The type to deserialize the result to</typeparam> /// <param name="script">Script to be evaluated in browser context</param> /// <param name="args">Arguments to pass to script</param> /// <remarks> /// If the script, returns a Promise, then the method would wait for the promise to resolve and return its value. /// <see cref="JSHandle"/> instances can be passed as arguments /// </remarks> /// <returns>Task which resolves to script return value</returns> public Task <T> EvaluateFunctionAsync <T>(string script, params object[] args) { var list = new List <object>(args); list.Insert(0, this); return(ExecutionContext.EvaluateFunctionAsync <T>(script, list.ToArray())); }
private async Task ScrollIntoViewIfNeededAsync() { var errorMessage = await ExecutionContext.EvaluateFunctionAsync <string>(@"async(element, pageJavascriptEnabled) => { if (!element.isConnected) return 'Node is detached from document'; if (element.nodeType !== Node.ELEMENT_NODE) return 'Node is not of type HTMLElement'; // force-scroll if page's javascript is disabled. if (!pageJavascriptEnabled) { element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'}); return false; } const visibleRatio = await new Promise(resolve => { const observer = new IntersectionObserver(entries => { resolve(entries[0].intersectionRatio); observer.disconnect(); }); observer.observe(element); }); if (visibleRatio !== 1.0) element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'}); return false; }", this, Page.JavascriptEnabled).ConfigureAwait(false); if (errorMessage != null) { throw new PuppeteerException(errorMessage); } }
/// <summary> /// This method scrolls element into view if needed, and then uses <seealso cref="Page.ScreenshotBase64Async(ScreenshotOptions)"/> to take a screenshot of the element. /// If the element is detached from DOM, the method throws an error. /// </summary> /// <returns>Task which resolves to a <see cref="string"/> containing the image data as base64.</returns> /// <param name="options">Screenshot options.</param> public async Task <string> ScreenshotBase64Async(ScreenshotOptions options) { var needsViewportReset = false; var boundingBox = await BoundingBoxAsync().ConfigureAwait(false); if (boundingBox == null) { throw new PuppeteerException("Node is either not visible or not an HTMLElement"); } var viewport = Page.Viewport; if (viewport != null && (boundingBox.Width > viewport.Width || boundingBox.Height > viewport.Height)) { var newRawViewport = JObject.FromObject(viewport); newRawViewport.Merge(new ViewPortOptions { Width = (int)Math.Max(viewport.Width, Math.Ceiling(boundingBox.Width)), Height = (int)Math.Max(viewport.Height, Math.Ceiling(boundingBox.Height)) }); await Page.SetViewportAsync(newRawViewport.ToObject <ViewPortOptions>()).ConfigureAwait(false); needsViewportReset = true; } await ExecutionContext.EvaluateFunctionAsync(@"function(element) { element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant'}); }", this).ConfigureAwait(false); await ScrollIntoViewIfNeededAsync().ConfigureAwait(false); boundingBox = await BoundingBoxAsync().ConfigureAwait(false); if (boundingBox == null) { throw new PuppeteerException("Node is either not visible or not an HTMLElement"); } var getLayoutMetricsResponse = await Client.SendAsync <GetLayoutMetricsResponse>("Page.getLayoutMetrics").ConfigureAwait(false); var clip = boundingBox; clip.X += getLayoutMetricsResponse.LayoutViewport.PageX; clip.Y += getLayoutMetricsResponse.LayoutViewport.PageY; options.Clip = boundingBox.ToClip(); var imageData = await Page.ScreenshotBase64Async(options).ConfigureAwait(false); if (needsViewportReset) { await Page.SetViewportAsync(viewport).ConfigureAwait(false); } return(imageData); }
/// <summary> /// Evaluates if the element is visible in the current viewport. /// </summary> /// <returns>A task which resolves to true if the element is visible in the current viewport.</returns> public Task <bool> IsIntersectingViewportAsync() => ExecutionContext.EvaluateFunctionAsync <bool>(@"async element => { const visibleRatio = await new Promise(resolve => { const observer = new IntersectionObserver(entries => { resolve(entries[0].intersectionRatio); observer.disconnect(); }); observer.observe(element); }); return visibleRatio > 0; }", this);
private async Task ScrollIntoViewIfNeededAsync() { var errorMessage = await ExecutionContext.EvaluateFunctionAsync <string>(@"element => { if (!element.isConnected) return 'Node is detached from document'; if (element.nodeType !== Node.ELEMENT_NODE) return 'Node is not of type HTMLElement'; element.scrollIntoViewIfNeeded(); return null; }", this); if (errorMessage != null) { throw new PuppeteerException(errorMessage); } }
/// <summary> /// This method scrolls element into view if needed, and then uses <seealso cref="Page.ScreenshotDataAsync(ScreenshotOptions)"/> to take a screenshot of the element. /// If the element is detached from DOM, the method throws an error. /// </summary> /// <returns>Task which resolves to a <see cref="byte"/>[] containing the image data.</returns> /// <param name="options">Screenshot options.</param> public async Task <byte[]> ScreenshotDataAsync(ScreenshotOptions options) { var needsViewportReset = false; var boundingBox = await AssertBoundingBoxAsync(); var viewport = Page.Viewport; if (boundingBox.Width > viewport.Width || boundingBox.Height > viewport.Height) { var newRawViewport = JObject.FromObject(viewport); newRawViewport.Merge(new ViewPortOptions { Width = (int)Math.Max(viewport.Width, Math.Ceiling(boundingBox.Width)), Height = (int)Math.Max(viewport.Height, Math.Ceiling(boundingBox.Height)) }); await Page.SetViewportAsync(newRawViewport.ToObject <ViewPortOptions>()); needsViewportReset = true; } await ExecutionContext.EvaluateFunctionAsync(@"function(element) { element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant'}); }", this); boundingBox = await AssertBoundingBoxAsync(); var getLayoutMetricsResponse = await Client.SendAsync <GetLayoutMetricsResponse>("Page.getLayoutMetrics"); var clip = boundingBox; clip.X += getLayoutMetricsResponse.LayoutViewport.PageX; clip.Y += getLayoutMetricsResponse.LayoutViewport.PageY; options.Clip = boundingBox.ToClip(); var imageData = await Page.ScreenshotDataAsync(options); if (needsViewportReset) { await Page.SetViewportAsync(viewport); } return(imageData); }
/// <summary> /// Calls <c>focus</c> <see cref="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus"/> on the element. /// </summary> /// <returns>Task</returns> public Task FocusAsync() => ExecutionContext.EvaluateFunctionAsync("element => element.focus()", this);