/*
* MIT License
*
* Copyright (c) 2020 DarÃo Kondratiuk
* Copyright (c) 2020 Meir Blachman
* Modifications copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Playwright.Helpers;
using Microsoft.Playwright.Transport;
using Microsoft.Playwright.Transport.Channels;
using Microsoft.Playwright.Transport.Protocol;
namespace Microsoft.Playwright.Core
{
internal class Frame : ChannelOwnerBase, IChannelOwner<Frame>, IFrame
{
internal readonly FrameChannel _channel;
private readonly List<WaitUntilState> _loadStates = new();
internal Frame(IChannelOwner parent, string guid, FrameInitializer initializer) : base(parent, guid)
{
_channel = new(guid, parent.Connection, this);
Url = initializer.Url;
Name = initializer.Name;
ParentFrame = initializer.ParentFrame;
_loadStates = initializer.LoadStates;
_channel.LoadState += (_, e) =>
{
lock (_loadStates)
{
if (e.Add.HasValue)
{
_loadStates.Add(e.Add.Value);
LoadState?.Invoke(this, e.Add.Value);
}
if (e.Remove.HasValue)
{
_loadStates.Remove(e.Remove.Value);
}
}
};
_channel.Navigated += (_, e) =>
{
Url = e.Url;
Name = e.Name;
Navigated?.Invoke(this, e);
if (string.IsNullOrEmpty(e.Error))
{
((Page)Page)?.OnFrameNavigated(this);
}
};
}
/// <summary>
/// Raised when a navigation is received.
/// </summary>
public event EventHandler<FrameNavigatedEventArgs> Navigated;
/// <summary>
/// Raised when a new LoadState was added.
/// </summary>
public event EventHandler<WaitUntilState> LoadState;
ChannelBase IChannelOwner.Channel => _channel;
IChannel<Frame> IChannelOwner<Frame>.Channel => _channel;
public IReadOnlyList<IFrame> ChildFrames => ChildFramesList;
public string Name { get; internal set; }
public string Url { get; internal set; }
IFrame IFrame.ParentFrame => ParentFrame;
public Frame ParentFrame { get; }
public IPage Page { get; internal set; }
public bool IsDetached { get; internal set; }
internal List<Frame> ChildFramesList { get; } = new();
public async Task<IElementHandle> FrameElementAsync()
=> (await _channel.FrameElementAsync().ConfigureAwait(false)).Object;
public IFrameLocator FrameLocator(string selector)
=> new FrameLocator(this, selector);
public Task<string> TitleAsync() => _channel.TitleAsync();
public Task WaitForTimeoutAsync(float timeout)
=> _channel.WaitForTimeoutAsync(timeout);
public Task<IReadOnlyList<string>> SelectOptionAsync(string selector, string values, FrameSelectOptionOptions options = default)
=> SelectOptionAsync(selector, new[] { values }, options);
public Task<IReadOnlyList<string>> SelectOptionAsync(string selector, IEnumerable<string> values, FrameSelectOptionOptions options = default)
=> SelectOptionAsync(selector, values.Select(x => new SelectOptionValue() { Value = x }), options);
public Task<IReadOnlyList<string>> SelectOptionAsync(string selector, IElementHandle values, FrameSelectOptionOptions options = default)
=> SelectOptionAsync(selector, new[] { values }, options);
public async Task<IReadOnlyList<string>> SelectOptionAsync(string selector, IEnumerable<IElementHandle> values, FrameSelectOptionOptions options = default)
=> (await _channel.SelectOptionAsync(
selector,
values.Select(x => x as ElementHandle),
noWaitAfter: options?.NoWaitAfter,
strict: options?.Strict,
force: options?.Force,
timeout: options?.Timeout).ConfigureAwait(false)).ToList().AsReadOnly();
public Task<IReadOnlyList<string>> SelectOptionAsync(string selector, SelectOptionValue values, FrameSelectOptionOptions options = default)
=> SelectOptionAsync(selector, new[] { values }, options);
public async Task<IReadOnlyList<string>> SelectOptionAsync(string selector, IEnumerable<SelectOptionValue> values, FrameSelectOptionOptions options = default)
=> (await _channel.SelectOptionAsync(
selector,
values,
noWaitAfter: options?.NoWaitAfter,
strict: options?.Strict,
force: options?.Force,
timeout: options?.Timeout).ConfigureAwait(false)).ToList().AsReadOnly();
public async Task WaitForLoadStateAsync(LoadState? state = default, FrameWaitForLoadStateOptions options = default)
{
Task<WaitUntilState> task;
Waiter waiter = null;
WaitUntilState loadState = Microsoft.Playwright.WaitUntilState.Load;
switch (state)
{
case Microsoft.Playwright.LoadState.Load:
loadState = Microsoft.Playwright.WaitUntilState.Load;
break;
case Microsoft.Playwright.LoadState.DOMContentLoaded:
loadState = Microsoft.Playwright.WaitUntilState.DOMContentLoaded;
break;
case Microsoft.Playwright.LoadState.NetworkIdle:
loadState = Microsoft.Playwright.WaitUntilState.NetworkIdle;
break;
}
try
{
lock (_loadStates)
{
if (_loadStates.Contains(loadState))
{
return;
}
waiter = SetupNavigationWaiter("frame.WaitForLoadStateAsync", options?.Timeout);
task = waiter.WaitForEventAsync<WaitUntilState>(this, "LoadState", s =>
{
waiter.Log($" \"{s}\" event fired");
return s == loadState;
});
}
await task.ConfigureAwait(false);
}
finally
{
waiter?.Dispose();
}
}
public async Task<IResponse> WaitForNavigationAsync(FrameWaitForNavigationOptions options = default)
{
WaitUntilState waitUntil2 = options?.WaitUntil ?? WaitUntilState.Load;
using var waiter = SetupNavigationWaiter("frame.WaitForNavigationAsync", options?.Timeout);
string toUrl = !string.IsNullOrEmpty(options?.UrlString) ? $" to \"{options?.UrlString}\"" : string.Empty;
waiter.Log($"waiting for navigation{toUrl} until \"{waitUntil2}\"");
var navigatedEventTask = waiter.WaitForEventAsync<FrameNavigatedEventArgs>(
this,
"Navigated",
e =>
{
// Any failed navigation results in a rejection.
if (e.Error != null)
{
return true;
}
waiter.Log($" navigated to \"{e.Url}\"");
return UrlMatches(e.Url, options?.UrlString, options?.UrlRegex, options?.UrlFunc);
});
var navigatedEvent = await navigatedEventTask.ConfigureAwait(false);
if (navigatedEvent.Error != null)
{
var ex = new PlaywrightException(navigatedEvent.Error);
await waiter.WaitForPromiseAsync(Task.FromException<object>(ex)).ConfigureAwait(false);
}
if (!_loadStates.Select(s => s.ToValueString()).Contains(waitUntil2.ToValueString()))
{
await waiter.WaitForEventAsync<WaitUntilState>(
this,
"LoadState",
e =>
{
waiter.Log($" \"{e}\" event fired");
return e.ToValueString() == waitUntil2.ToValueString();
}).ConfigureAwait(false);
}
var request = navigatedEvent.NewDocument?.Request?.Object;
var response = request != null
? await waiter.WaitForPromiseAsync(request.FinalRequest.ResponseAsync()).ConfigureAwait(false)
: null;
return response;
}
public async Task<IResponse> RunAndWaitForNavigationAsync(Func<Task> action, FrameRunAndWaitForNavigationOptions options = default)
{
var result = WaitForNavigationAsync(new()
{
UrlString = options?.UrlString,
UrlRegex = options?.UrlRegex,
UrlFunc = options?.UrlFunc,
WaitUntil = options?.WaitUntil,
Timeout = options?.Timeout,
});
if (action != null)
{
await WrapApiBoundaryAsync(() => Task.WhenAll(result, action())).ConfigureAwait(false);
}
return await result.ConfigureAwait(false);
}
public Task TapAsync(string selector, FrameTapOptions options = default)
=> _channel.TapAsync(
selector,
modifiers: options?.Modifiers,
position: options?.Position,
timeout: options?.Timeout,
force: options?.Force,
noWaitAfter: options?.NoWaitAfter,
trial: options?.Trial,
strict: options?.Strict);
internal Task<int> QueryCountAsync(string selector)
=> _channel.QueryCountAsync(selector);
public Task<string> ContentAsync() => _channel.ContentAsync();
public Task FocusAsync(string selector, FrameFocusOptions options = default)
=> _channel.FocusAsync(selector, options?.Timeout, options?.Strict);
public Task TypeAsync(string selector, string text, FrameTypeOptions options = default)
=> _channel.TypeAsync(
selector,
text,
delay: options?.Delay,
timeout: options?.Timeout,
noWaitAfter: options?.NoWaitAfter,
strict: options?.Strict);
public Task<string> GetAttributeAsync(string selector, string name, FrameGetAttributeOptions options = default)
=> _channel.GetAttributeAsync(selector, name, options?.Timeout, options?.Strict);
public Task<string> InnerHTMLAsync(string selector, FrameInnerHTMLOptions options = default)
=> _channel.InnerHTMLAsync(selector, options?.Timeout, options?.Strict);
public Task<string> InnerTextAsync(string selector, FrameInnerTextOptions options = default)
=> _channel.InnerTextAsync(selector, options?.Timeout, options?.Strict);
public Task<string> TextContentAsync(string selector, FrameTextContentOptions options = default)
=> _channel.TextContentAsync(selector, options?.Timeout, options?.Strict);
public Task HoverAsync(string selector, FrameHoverOptions options = default)
=> _channel.HoverAsync(
selector,
position: options?.Position,
modifiers: options?.Modifiers,
force: options?.Force,
timeout: options?.Timeout,
trial: options?.Trial,
strict: options?.Strict);
public Task PressAsync(string selector, string key, FramePressOptions options = default)
=> _channel.PressAsync(
selector,
key,
delay: options?.Delay,
timeout: options?.Timeout,
noWaitAfter: options?.NoWaitAfter,
strict: options?.Strict);
public Task DispatchEventAsync(string selector, string type, object eventInit = default, FrameDispatchEventOptions options = default)
=> _channel.DispatchEventAsync(
selector,
type,
ScriptsHelper.SerializedArgument(eventInit),
options?.Timeout,
options?.Strict);
public Task FillAsync(string selector, string value, FrameFillOptions options = default)
=> _channel.FillAsync(selector, value, force: options?.Force, timeout: options?.Timeout, noWaitAfter: options?.NoWaitAfter, options?.Strict);
public async Task<IElementHandle> AddScriptTagAsync(FrameAddScriptTagOptions options = default)
{
var content = options?.Content;
if (!string.IsNullOrEmpty(options?.Path))
{
content = File.ReadAllText(options.Path);
content += "//# sourceURL=" + options.Path.Replace("\n", string.Empty);
}
return (await _channel.AddScriptTagAsync(options?.Url, options?.Path, content, options?.Type).ConfigureAwait(false)).Object;
}
public async Task<IElementHandle> AddStyleTagAsync(FrameAddStyleTagOptions options = default)
{
var content = options?.Content;
if (!string.IsNullOrEmpty(options?.Path))
{
content = File.ReadAllText(options.Path);
content += "//# sourceURL=" + options.Path.Replace("\n", string.Empty);
}
return (await _channel.AddStyleTagAsync(options?.Url, options?.Path, content).ConfigureAwait(false)).Object;
}
public Task SetInputFilesAsync(string selector, string files, FrameSetInputFilesOptions options = default)
=> SetInputFilesAsync(selector, new[] { files }, options);
public async Task SetInputFilesAsync(string selector, IEnumerable<string> files, FrameSetInputFilesOptions options = default)
{
var converted = await SetInputFilesHelpers.ConvertInputFilesAsync(files, (BrowserContext)Page.Context).ConfigureAwait(false);
if (converted.Files != null)
{
await _channel.SetInputFilesAsync(selector, converted.Files, options?.NoWaitAfter, options?.Timeout, options?.Strict).ConfigureAwait(false);
}
else
{
await _channel.SetInputFilePathsAsync(selector, converted?.LocalPaths, converted?.Streams, options?.NoWaitAfter, options?.Timeout, options?.Strict).ConfigureAwait(false);
}
}
public Task SetInputFilesAsync(string selector, FilePayload files, FrameSetInputFilesOptions options = default)
=> SetInputFilesAsync(selector, new[] { files }, options);
public async Task SetInputFilesAsync(string selector, IEnumerable<FilePayload> files, FrameSetInputFilesOptions options = default)
{
var converted = SetInputFilesHelpers.ConvertInputFiles(files);
await _channel.SetInputFilesAsync(selector, converted.Files, noWaitAfter: options?.NoWaitAfter, timeout: options?.Timeout, options?.Strict).ConfigureAwait(false);
}
public Task ClickAsync(string selector, FrameClickOptions options = default)
=> _channel.ClickAsync(
selector,
delay: options?.Delay,
button: options?.Button,
clickCount: options?.ClickCount,
modifiers: options?.Modifiers,
position: options?.Position,
timeout: options?.Timeout,
force: options?.Force,
noWaitAfter: options?.NoWaitAfter,
trial: options?.Trial,
strict: options?.Strict);
public Task DblClickAsync(string selector, FrameDblClickOptions options = default)
=> _channel.DblClickAsync(
selector,
delay: options?.Delay,
button: options?.Button,
position: options?.Position,
modifiers: options?.Modifiers,
timeout: options?.Timeout,
force: options?.Force,
noWaitAfter: options?.NoWaitAfter,
trial: options?.Trial,
strict: options?.Strict);
public Task CheckAsync(string selector, FrameCheckOptions options = default)
=> _channel.CheckAsync(
selector,
position: options?.Position,
timeout: options?.Timeout,
force: options?.Force,
noWaitAfter: options?.NoWaitAfter,
trial: options?.Trial,
strict: options?.Strict);
public Task UncheckAsync(string selector, FrameUncheckOptions options = default)
=> _channel.UncheckAsync(
selector,
position: options?.Position,
timeout: options?.Timeout,
force: options?.Force,
noWaitAfter: options?.NoWaitAfter,
trial: options?.Trial,
strict: options?.Strict);
public Task SetCheckedAsync(string selector, bool checkedState, FrameSetCheckedOptions options = null)
=> checkedState ?
_channel.CheckAsync(
selector,
position: options?.Position,
timeout: options?.Timeout,
force: options?.Force,
noWaitAfter: options?.NoWaitAfter,
trial: options?.Trial,
strict: options?.Strict)
: _channel.UncheckAsync(
selector,
position: options?.Position,
timeout: options?.Timeout,
force: options?.Force,
noWaitAfter: options?.NoWaitAfter,
trial: options?.Trial,
strict: options?.Strict);
public Task SetContentAsync(string html, FrameSetContentOptions options = default)
=> _channel.SetContentAsync(html, timeout: options?.Timeout, waitUntil: options?.WaitUntil);
public Task<string> InputValueAsync(string selector, FrameInputValueOptions options = null)
=> _channel.InputValueAsync(selector, options?.Timeout, options?.Strict);
public async Task<IElementHandle> QuerySelectorAsync(string selector)
=> (await _channel.QuerySelectorAsync(selector).ConfigureAwait(false))?.Object;
public async Task<IReadOnlyList<IElementHandle>> QuerySelectorAllAsync(string selector)
=> (await _channel.QuerySelectorAllAsync(selector).ConfigureAwait(false)).Select(c => ((ElementHandleChannel)c).Object).ToList().AsReadOnly();
public async Task<IJSHandle> WaitForFunctionAsync(string expression, object arg = default, FrameWaitForFunctionOptions options = default)
=> (await _channel.WaitForFunctionAsync(
expression: expression,
arg: ScriptsHelper.SerializedArgument(arg),
timeout: options?.Timeout,
polling: options?.PollingInterval).ConfigureAwait(false)).Object;
public async Task<IElementHandle> WaitForSelectorAsync(string selector, FrameWaitForSelectorOptions options = default)
=> (await _channel.WaitForSelectorAsync(
selector: selector,
state: options?.State,
timeout: options?.Timeout,
strict: options?.Strict,
omitReturnValue: false).ConfigureAwait(false))?.Object;
public async Task<IElementHandle> LocatorWaitForAsync(string selector, LocatorWaitForOptions options = default)
=> (await _channel.WaitForSelectorAsync(
selector: selector,
state: options?.State,
timeout: options?.Timeout,
strict: true,
omitReturnValue: true).ConfigureAwait(false))?.Object;
public async Task<IJSHandle> EvaluateHandleAsync(string script, object args = null)
=> (await _channel.EvaluateExpressionHandleAsync(
script,
arg: ScriptsHelper.SerializedArgument(args)).ConfigureAwait(false))?.Object;
public async Task<JsonElement?> EvaluateAsync(string script, object arg = null)
=> ScriptsHelper.ParseEvaluateResult<JsonElement?>(await _channel.EvaluateExpressionAsync(
script,
arg: ScriptsHelper.SerializedArgument(arg)).ConfigureAwait(false));
public async Task<T> EvaluateAsync<T>(string script, object arg = null)
=> ScriptsHelper.ParseEvaluateResult<T>(await _channel.EvaluateExpressionAsync(
script,
arg: ScriptsHelper.SerializedArgument(arg)).ConfigureAwait(false));
public async Task<JsonElement?> EvalOnSelectorAsync(string selector, string script, object arg = null)
=> ScriptsHelper.ParseEvaluateResult<JsonElement?>(await _channel.EvalOnSelectorAsync(
selector: selector,
script,
arg: ScriptsHelper.SerializedArgument(arg),
strict: null).ConfigureAwait(false));
public async Task<T> EvalOnSelectorAsync<T>(string selector, string script, object arg = null)
=> ScriptsHelper.ParseEvaluateResult<T>(await _channel.EvalOnSelectorAsync(
selector: selector,
script,
arg: ScriptsHelper.SerializedArgument(arg),
strict: null).ConfigureAwait(false));
public async Task<T> EvalOnSelectorAsync<T>(string selector, string expression, object arg = null, FrameEvalOnSelectorOptions options = null)
=> ScriptsHelper.ParseEvaluateResult<T>(await _channel.EvalOnSelectorAsync(
selector: selector,
expression,
arg: ScriptsHelper.SerializedArgument(arg),
strict: options?.Strict).ConfigureAwait(false));
public async Task<JsonElement?> EvalOnSelectorAllAsync(string selector, string script, object arg = null)
=> ScriptsHelper.ParseEvaluateResult<JsonElement?>(await _channel.EvalOnSelectorAllAsync(
selector: selector,
script,
arg: ScriptsHelper.SerializedArgument(arg)).ConfigureAwait(false));
public async Task<T> EvalOnSelectorAllAsync<T>(string selector, string script, object arg = null)
=> ScriptsHelper.ParseEvaluateResult<T>(await _channel.EvalOnSelectorAllAsync(
selector: selector,
script,
arg: ScriptsHelper.SerializedArgument(arg)).ConfigureAwait(false));
public ILocator Locator(string selector, FrameLocatorOptions options = null) => new Locator(this, selector, new() { HasTextRegex = options?.HasTextRegex, HasTextString = options?.HasTextString, Has = options?.Has });
public async Task<IElementHandle> QuerySelectorAsync(string selector, FrameQuerySelectorOptions options = null)
=> (await _channel.QuerySelectorAsync(selector, options?.Strict).ConfigureAwait(false))?.Object;
public async Task<IResponse> GotoAsync(string url, FrameGotoOptions options = default)
=> (await _channel.GotoAsync(
url,
timeout: options?.Timeout,
waitUntil: options?.WaitUntil,
referer: options?.Referer).ConfigureAwait(false))?.Object;
public Task<bool> IsCheckedAsync(string selector, FrameIsCheckedOptions options = default)
=> _channel.IsCheckedAsync(selector, timeout: options?.Timeout, options?.Strict);
public Task<bool> IsDisabledAsync(string selector, FrameIsDisabledOptions options = default)
=> _channel.IsDisabledAsync(selector, timeout: options?.Timeout, options?.Strict);
public Task<bool> IsEditableAsync(string selector, FrameIsEditableOptions options = default)
=> _channel.IsEditableAsync(selector, timeout: options?.Timeout, options?.Strict);
public Task<bool> IsEnabledAsync(string selector, FrameIsEnabledOptions options = default)
=> _channel.IsEnabledAsync(selector, timeout: options?.Timeout, options?.Strict);
#pragma warning disable CS0612 // Type or member is obsolete
public Task<bool> IsHiddenAsync(string selector, FrameIsHiddenOptions options = default)
=> _channel.IsHiddenAsync(selector, timeout: options?.Timeout, options?.Strict);
public Task<bool> IsVisibleAsync(string selector, FrameIsVisibleOptions options = default)
=> _channel.IsVisibleAsync(selector, timeout: options?.Timeout, options?.Strict);
#pragma warning restore CS0612 // Type or member is obsolete
public Task WaitForURLAsync(string url, FrameWaitForURLOptions options = default)
=> WaitForURLAsync(url, null, null, options);
public Task WaitForURLAsync(Regex url, FrameWaitForURLOptions options = default)
=> WaitForURLAsync(null, url, null, options);
public Task WaitForURLAsync(Func<string, bool> url, FrameWaitForURLOptions options = default)
=> WaitForURLAsync(null, null, url, options);
public Task DragAndDropAsync(string source, string target, FrameDragAndDropOptions options = null)
=> _channel.DragAndDropAsync(source, target, options?.Force, options?.NoWaitAfter, options?.Timeout, options?.Trial, options?.Strict);
internal Task<FrameExpectResult> ExpectAsync(string selector, string expression, FrameExpectOptions options = null) =>
_channel.ExpectAsync(selector, expression, expressionArg: options?.ExpressionArg, expectedText: options?.ExpectedText, expectedNumber: options?.ExpectedNumber, expectedValue: options?.ExpectedValue, useInnerText: options?.UseInnerText, isNot: options?.IsNot, timeout: options?.Timeout);
private Task WaitForURLAsync(string urlString, Regex urlRegex, Func<string, bool> urlFunc, FrameWaitForURLOptions options = default)
{
if (UrlMatches(Url, urlString, urlRegex, urlFunc))
{
return WaitForLoadStateAsync(ToLoadState(options?.WaitUntil), new() { Timeout = options?.Timeout });
}
return WaitForNavigationAsync(
new()
{
UrlString = urlString,
UrlRegex = urlRegex,
UrlFunc = urlFunc,
Timeout = options?.Timeout,
WaitUntil = options?.WaitUntil,
});
}
private LoadState? ToLoadState(WaitUntilState? waitUntilState)
{
if (waitUntilState == null)
{
return null;
}
return waitUntilState switch
{
WaitUntilState.Load => Microsoft.Playwright.LoadState.Load,
WaitUntilState.DOMContentLoaded => Microsoft.Playwright.LoadState.DOMContentLoaded,
WaitUntilState.NetworkIdle => Microsoft.Playwright.LoadState.NetworkIdle,
_ => null,
};
}
private Waiter SetupNavigationWaiter(string @event, float? timeout)
{
var waiter = new Waiter(this.Page as Page, @event);
if (this.Page.IsClosed)
{
waiter.RejectImmediately(new PlaywrightException("Navigation failed because page was closed!"));
}
waiter.RejectOnEvent<IPage>(Page, PageEvent.Close.Name, new("Navigation failed because page was closed!"));
waiter.RejectOnEvent<IPage>(Page, PageEvent.Crash.Name, new("Navigation failed because page was crashed!"));
waiter.RejectOnEvent<IFrame>(
Page,
"FrameDetached",
new("Navigating frame was detached!"),
e => e == this);
timeout ??= (Page as Page)?.DefaultNavigationTimeout ?? PlaywrightImpl.DefaultTimeout;
waiter.RejectOnTimeout(Convert.ToInt32(timeout), $"Timeout {timeout}ms exceeded.");
return waiter;
}
private bool UrlMatches(string url, string matchUrl, Regex regex, Func<string, bool> match)
{
matchUrl = (Page.Context as BrowserContext)?.CombineUrlWithBase(matchUrl);
if (matchUrl == null && regex == null && match == null)
{
return true;
}
if (!string.IsNullOrEmpty(matchUrl))
{
regex = new(matchUrl.GlobToRegex());
}
if (matchUrl != null && url == matchUrl)
{
return true;
}
if (regex != null)
{
return regex.IsMatch(url);
}
return match(url);
}
internal Task HighlightAsync(string selector)
=> _channel.HighlightAsync(selector);
}
}
/*
* MIT License
*
* Copyright (c) 2020 DarÃo Kondratiuk
* Copyright (c) 2020 Meir Blachman
* Modifications copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Playwright.Helpers;
using Microsoft.Playwright.Transport.Protocol;
namespace Microsoft.Playwright.Core
{
internal class Locator : ILocator
{
internal readonly Frame _frame;
internal readonly string _selector;
private readonly LocatorLocatorOptions _options;
public Locator(Frame parent, string selector, LocatorLocatorOptions options = null)
{
_frame = parent;
_selector = selector;
_options = options;
if (options?.HasTextRegex != null)
{
_selector += $" >> :scope:text-matches({options.HasTextRegex.ToString().EscapeWithQuotes("\"")}, {options.HasTextRegex.Options.GetInlineFlags().EscapeWithQuotes("\"")})";
}
if (options?.HasTextString != null)
{
_selector += $" >> :scope:has-text({options.HasTextString.EscapeWithQuotes("\"")})";
}
if (options?.Has != null)
{
var has = (Locator)options.Has;
if (has._frame != _frame)
{
throw new ArgumentException("Inner \"has\" locator must belong to the same frame.");
}
_selector += " >> has=" + JsonSerializer.Serialize(has._selector);
}
}
public ILocator First => new Locator(_frame, $"{_selector} >> nth=0");
public ILocator Last => new Locator(_frame, $"{_selector} >> nth=-1");
IPage ILocator.Page => _frame.Page;
public async Task<IReadOnlyList<string>> AllInnerTextsAsync()
=> await EvaluateAllAsync<string[]>("ee => ee.map(e => e.innerText)").ConfigureAwait(false);
public async Task<IReadOnlyList<string>> AllTextContentsAsync()
=> await EvaluateAllAsync<string[]>("ee => ee.map(e => e.textContent || '')").ConfigureAwait(false);
public async Task<LocatorBoundingBoxResult> BoundingBoxAsync(LocatorBoundingBoxOptions options = null)
=> await WithElementAsync(
async (h, _) =>
{
var bb = await h.BoundingBoxAsync().ConfigureAwait(false);
if (bb == null)
{
return null;
}
return new LocatorBoundingBoxResult()
{
Height = bb.Height,
Width = bb.Width,
X = bb.X,
Y = bb.Y,
};
},
options).ConfigureAwait(false);
public Task CheckAsync(LocatorCheckOptions options = null)
=> _frame.CheckAsync(
_selector,
ConvertOptions<FrameCheckOptions>(options));
public Task ClickAsync(LocatorClickOptions options = null)
=> _frame.ClickAsync(
_selector,
ConvertOptions<FrameClickOptions>(options));
public Task SetCheckedAsync(bool checkedState, LocatorSetCheckedOptions options = null)
=> checkedState ?
CheckAsync(ConvertOptions<LocatorCheckOptions>(options))
: UncheckAsync(ConvertOptions<LocatorUncheckOptions>(options));
public Task<int> CountAsync()
=> _frame.QueryCountAsync(_selector);
public Task DblClickAsync(LocatorDblClickOptions options = null)
=> _frame.DblClickAsync(_selector, ConvertOptions<FrameDblClickOptions>(options));
public Task DispatchEventAsync(string type, object eventInit = null, LocatorDispatchEventOptions options = null)
=> _frame.DispatchEventAsync(_selector, type, eventInit, ConvertOptions<FrameDispatchEventOptions>(options));
public Task DragToAsync(ILocator target, LocatorDragToOptions options = null)
=> _frame.DragAndDropAsync(_selector, ((Locator)target)._selector, ConvertOptions<FrameDragAndDropOptions>(options));
public async Task<IElementHandle> ElementHandleAsync(LocatorElementHandleOptions options = null)
=> await _frame.WaitForSelectorAsync(
_selector,
ConvertOptions<FrameWaitForSelectorOptions>(options)).ConfigureAwait(false);
public Task<IReadOnlyList<IElementHandle>> ElementHandlesAsync()
=> _frame.QuerySelectorAllAsync(_selector);
public Task<T> EvaluateAllAsync<T>(string expression, object arg = null)
=> _frame.EvalOnSelectorAllAsync<T>(_selector, expression, arg);
public Task<JsonElement?> EvaluateAsync(string expression, object arg = null, LocatorEvaluateOptions options = null)
=> EvaluateAsync<JsonElement?>(expression, arg, options);
public Task<T> EvaluateAsync<T>(string expression, object arg = null, LocatorEvaluateOptions options = null)
=> _frame.EvalOnSelectorAsync<T>(_selector, expression, arg, ConvertOptions<FrameEvalOnSelectorOptions>(options));
public async Task<IJSHandle> EvaluateHandleAsync(string expression, object arg = null, LocatorEvaluateHandleOptions options = null)
=> await WithElementAsync(async (e, _) => await e.EvaluateHandleAsync(expression, arg).ConfigureAwait(false), options).ConfigureAwait(false);
public async Task FillAsync(string value, LocatorFillOptions options = null)
=> await _frame.FillAsync(_selector, value, ConvertOptions<FrameFillOptions>(options)).ConfigureAwait(false);
public Task FocusAsync(LocatorFocusOptions options = null)
=> _frame.FocusAsync(_selector, ConvertOptions<FrameFocusOptions>(options));
IFrameLocator ILocator.FrameLocator(string selector) =>
new FrameLocator(_frame, $"{_selector} >> {selector}");
public Task<string> GetAttributeAsync(string name, LocatorGetAttributeOptions options = null)
=> _frame.GetAttributeAsync(_selector, name, ConvertOptions<FrameGetAttributeOptions>(options));
public Task HoverAsync(LocatorHoverOptions options = null)
=> _frame.HoverAsync(_selector, ConvertOptions<FrameHoverOptions>(options));
public Task<string> InnerHTMLAsync(LocatorInnerHTMLOptions options = null)
=> _frame.InnerHTMLAsync(_selector, ConvertOptions<FrameInnerHTMLOptions>(options));
public Task<string> InnerTextAsync(LocatorInnerTextOptions options = null)
=> _frame.InnerTextAsync(_selector, ConvertOptions<FrameInnerTextOptions>(options));
public Task<string> InputValueAsync(LocatorInputValueOptions options = null)
=> _frame.InputValueAsync(_selector, ConvertOptions<FrameInputValueOptions>(options));
public Task<bool> IsCheckedAsync(LocatorIsCheckedOptions options = null)
=> _frame.IsCheckedAsync(_selector, ConvertOptions<FrameIsCheckedOptions>(options));
public Task<bool> IsDisabledAsync(LocatorIsDisabledOptions options = null)
=> _frame.IsDisabledAsync(_selector, ConvertOptions<FrameIsDisabledOptions>(options));
public Task<bool> IsEditableAsync(LocatorIsEditableOptions options = null)
=> _frame.IsEditableAsync(_selector, ConvertOptions<FrameIsEditableOptions>(options));
public Task<bool> IsEnabledAsync(LocatorIsEnabledOptions options = null)
=> _frame.IsEnabledAsync(_selector, ConvertOptions<FrameIsEnabledOptions>(options));
public Task<bool> IsHiddenAsync(LocatorIsHiddenOptions options = null)
=> _frame.IsHiddenAsync(_selector, ConvertOptions<FrameIsHiddenOptions>(options));
public Task<bool> IsVisibleAsync(LocatorIsVisibleOptions options = null)
=> _frame.IsVisibleAsync(_selector, ConvertOptions<FrameIsVisibleOptions>(options));
public ILocator Nth(int index)
=> new Locator(_frame, $"{_selector} >> nth={index}");
public Task PressAsync(string key, LocatorPressOptions options = null)
=> _frame.PressAsync(_selector, key, ConvertOptions<FramePressOptions>(options));
public Task<byte[]> ScreenshotAsync(LocatorScreenshotOptions options = null)
=> WithElementAsync(async (h, o) => await h.ScreenshotAsync(ConvertOptions<ElementHandleScreenshotOptions>(o)).ConfigureAwait(false), options);
public Task ScrollIntoViewIfNeededAsync(LocatorScrollIntoViewIfNeededOptions options = null)
=> WithElementAsync(async (h, o) => await h.ScrollIntoViewIfNeededAsync(ConvertOptions<ElementHandleScrollIntoViewIfNeededOptions>(o)).ConfigureAwait(false), options);
public Task<IReadOnlyList<string>> SelectOptionAsync(string values, LocatorSelectOptionOptions options = null)
=> _frame.SelectOptionAsync(_selector, values, ConvertOptions<FrameSelectOptionOptions>(options));
public Task<IReadOnlyList<string>> SelectOptionAsync(IElementHandle values, LocatorSelectOptionOptions options = null)
=> _frame.SelectOptionAsync(_selector, values, ConvertOptions<FrameSelectOptionOptions>(options));
public Task<IReadOnlyList<string>> SelectOptionAsync(IEnumerable<string> values, LocatorSelectOptionOptions options = null)
=> _frame.SelectOptionAsync(_selector, values, ConvertOptions<FrameSelectOptionOptions>(options));
public Task<IReadOnlyList<string>> SelectOptionAsync(SelectOptionValue values, LocatorSelectOptionOptions options = null)
=> _frame.SelectOptionAsync(_selector, values, ConvertOptions<FrameSelectOptionOptions>(options));
public Task<IReadOnlyList<string>> SelectOptionAsync(IEnumerable<IElementHandle> values, LocatorSelectOptionOptions options = null)
=> _frame.SelectOptionAsync(_selector, values, ConvertOptions<FrameSelectOptionOptions>(options));
public Task<IReadOnlyList<string>> SelectOptionAsync(IEnumerable<SelectOptionValue> values, LocatorSelectOptionOptions options = null)
=> _frame.SelectOptionAsync(_selector, values, ConvertOptions<FrameSelectOptionOptions>(options));
public Task SelectTextAsync(LocatorSelectTextOptions options = null)
=> WithElementAsync((h, o) => h.SelectTextAsync(ConvertOptions<ElementHandleSelectTextOptions>(o)), options);
public Task SetInputFilesAsync(string files, LocatorSetInputFilesOptions options = null)
=> _frame.SetInputFilesAsync(_selector, files, ConvertOptions<FrameSetInputFilesOptions>(options));
public Task SetInputFilesAsync(IEnumerable<string> files, LocatorSetInputFilesOptions options = null)
=> _frame.SetInputFilesAsync(_selector, files, ConvertOptions<FrameSetInputFilesOptions>(options));
public Task SetInputFilesAsync(FilePayload files, LocatorSetInputFilesOptions options = null)
=> _frame.SetInputFilesAsync(_selector, files, ConvertOptions<FrameSetInputFilesOptions>(options));
public Task SetInputFilesAsync(IEnumerable<FilePayload> files, LocatorSetInputFilesOptions options = null)
=> _frame.SetInputFilesAsync(_selector, files, ConvertOptions<FrameSetInputFilesOptions>(options));
public Task TapAsync(LocatorTapOptions options = null)
=> _frame.TapAsync(_selector, ConvertOptions<FrameTapOptions>(options));
public Task<string> TextContentAsync(LocatorTextContentOptions options = null)
=> _frame.TextContentAsync(_selector, ConvertOptions<FrameTextContentOptions>(options));
public Task TypeAsync(string text, LocatorTypeOptions options = null)
=> _frame.TypeAsync(_selector, text, ConvertOptions<FrameTypeOptions>(options));
public Task UncheckAsync(LocatorUncheckOptions options = null)
=> _frame.UncheckAsync(_selector, ConvertOptions<FrameUncheckOptions>(options));
ILocator ILocator.Locator(string selector, LocatorLocatorOptions options)
=> new Locator(_frame, $"{_selector} >> {selector}", options);
public Task WaitForAsync(LocatorWaitForOptions options = null)
=> _frame.LocatorWaitForAsync(_selector, ConvertOptions<LocatorWaitForOptions>(options));
internal Task<FrameExpectResult> ExpectAsync(string expression, FrameExpectOptions options = null)
=> _frame.ExpectAsync(
_selector,
expression,
options);
public override string ToString() => "[email protected]" + _selector;
private T ConvertOptions<T>(object source)
where T : class, new()
{
T target = new();
var targetType = target.GetType();
if (source != null)
{
var sourceType = source.GetType();
foreach (var sourceProperty in sourceType.GetProperties())
{
var targetProperty = targetType.GetProperty(sourceProperty.Name);
if (targetProperty != null)
{
targetProperty.SetValue(target, sourceProperty.GetValue(source));
}
}
}
var strictProperty = targetType.GetProperty("Strict");
if (strictProperty != null && strictProperty.GetValue(target) == null)
{
strictProperty.SetValue(target, true);
}
return target;
}
private async Task<TResult> WithElementAsync<TOptions, TResult>(Func<IElementHandle, TOptions, Task<TResult>> callback, TOptions options)
where TOptions : class
where TResult : class
{
IElementHandle handle = await ElementHandleAsync(ConvertOptions<LocatorElementHandleOptions>(options)).ConfigureAwait(false);
try
{
return await callback(handle, options).ConfigureAwait(false);
}
finally
{
await handle.DisposeAsync().ConfigureAwait(false);
}
}
private async Task WithElementAsync<TOptions>(Func<IElementHandle, TOptions, Task> callback, TOptions options)
where TOptions : class
{
IElementHandle handle = await ElementHandleAsync(ConvertOptions<LocatorElementHandleOptions>(options)).ConfigureAwait(false);
try
{
await callback(handle, options).ConfigureAwait(false);
}
finally
{
await handle.DisposeAsync().ConfigureAwait(false);
}
}
public Task HighlightAsync() => _frame.HighlightAsync(_selector);
}
}