dt_automate/vendor/github.com/playwright-community/playwright-go/frame.go
2025-02-19 18:30:19 +08:00

788 lines
21 KiB
Go

package playwright
import (
"errors"
"fmt"
"os"
"time"
mapset "github.com/deckarep/golang-set/v2"
)
type frameImpl struct {
channelOwner
detached bool
page *pageImpl
name string
url string
parentFrame Frame
childFrames []Frame
loadStates mapset.Set[string]
}
func newFrame(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *frameImpl {
var loadStates mapset.Set[string]
if ls, ok := initializer["loadStates"].([]string); ok {
loadStates = mapset.NewSet[string](ls...)
} else {
loadStates = mapset.NewSet[string]()
}
f := &frameImpl{
name: initializer["name"].(string),
url: initializer["url"].(string),
loadStates: loadStates,
childFrames: make([]Frame, 0),
}
f.createChannelOwner(f, parent, objectType, guid, initializer)
channelOwner := fromNullableChannel(initializer["parentFrame"])
if channelOwner != nil {
f.parentFrame = channelOwner.(*frameImpl)
f.parentFrame.(*frameImpl).childFrames = append(f.parentFrame.(*frameImpl).childFrames, f)
}
f.channel.On("navigated", f.onFrameNavigated)
f.channel.On("loadstate", f.onLoadState)
return f
}
func (f *frameImpl) URL() string {
f.RLock()
defer f.RUnlock()
return f.url
}
func (f *frameImpl) Name() string {
f.RLock()
defer f.RUnlock()
return f.name
}
func (f *frameImpl) SetContent(content string, options ...FrameSetContentOptions) error {
_, err := f.channel.Send("setContent", map[string]interface{}{
"html": content,
}, options)
return err
}
func (f *frameImpl) Content() (string, error) {
content, err := f.channel.Send("content")
if content == nil {
return "", err
}
return content.(string), err
}
func (f *frameImpl) Goto(url string, options ...FrameGotoOptions) (Response, error) {
channel, err := f.channel.Send("goto", map[string]interface{}{
"url": url,
}, options)
if err != nil {
return nil, fmt.Errorf("Frame.Goto %s: %w", url, err)
}
channelOwner := fromNullableChannel(channel)
if channelOwner == nil {
// navigation to about:blank or navigation to the same URL with a different hash
return nil, nil
}
return channelOwner.(*responseImpl), nil
}
func (f *frameImpl) AddScriptTag(options FrameAddScriptTagOptions) (ElementHandle, error) {
if options.Path != nil {
file, err := os.ReadFile(*options.Path)
if err != nil {
return nil, err
}
options.Content = String(string(file))
options.Path = nil
}
channel, err := f.channel.Send("addScriptTag", options)
if err != nil {
return nil, err
}
return fromChannel(channel).(*elementHandleImpl), nil
}
func (f *frameImpl) AddStyleTag(options FrameAddStyleTagOptions) (ElementHandle, error) {
if options.Path != nil {
file, err := os.ReadFile(*options.Path)
if err != nil {
return nil, err
}
options.Content = String(string(file))
options.Path = nil
}
channel, err := f.channel.Send("addStyleTag", options)
if err != nil {
return nil, err
}
return fromChannel(channel).(*elementHandleImpl), nil
}
func (f *frameImpl) Page() Page {
return f.page
}
func (f *frameImpl) WaitForLoadState(options ...FrameWaitForLoadStateOptions) error {
option := FrameWaitForLoadStateOptions{}
if len(options) == 1 {
option = options[0]
}
if option.State == nil {
option.State = LoadStateLoad
}
return f.waitForLoadStateImpl(string(*option.State), option.Timeout, nil)
}
func (f *frameImpl) waitForLoadStateImpl(state string, timeout *float64, cb func() error) error {
if f.loadStates.ContainsOne(state) {
return nil
}
waiter, err := f.setNavigationWaiter(timeout)
if err != nil {
return err
}
waiter.WaitForEvent(f, "loadstate", func(payload interface{}) bool {
gotState := payload.(string)
return gotState == state
})
if cb == nil {
_, err := waiter.Wait()
return err
} else {
_, err := waiter.RunAndWait(cb)
return err
}
}
func (f *frameImpl) WaitForURL(url interface{}, options ...FrameWaitForURLOptions) error {
if f.page == nil {
return errors.New("frame is detached")
}
matcher := newURLMatcher(url, f.page.browserContext.options.BaseURL)
if matcher.Matches(f.URL()) {
state := "load"
timeout := Float(f.page.timeoutSettings.NavigationTimeout())
if len(options) == 1 {
if options[0].WaitUntil != nil {
state = string(*options[0].WaitUntil)
}
if options[0].Timeout != nil {
timeout = options[0].Timeout
}
}
return f.waitForLoadStateImpl(state, timeout, nil)
}
navigationOptions := FrameExpectNavigationOptions{URL: url}
if len(options) > 0 {
navigationOptions.Timeout = options[0].Timeout
navigationOptions.WaitUntil = options[0].WaitUntil
}
if _, err := f.ExpectNavigation(nil, navigationOptions); err != nil {
return err
}
return nil
}
func (f *frameImpl) ExpectNavigation(cb func() error, options ...FrameExpectNavigationOptions) (Response, error) {
if f.page == nil {
return nil, errors.New("frame is detached")
}
option := FrameExpectNavigationOptions{}
if len(options) == 1 {
option = options[0]
}
if option.WaitUntil == nil {
option.WaitUntil = WaitUntilStateLoad
}
if option.Timeout == nil {
option.Timeout = Float(f.page.timeoutSettings.NavigationTimeout())
}
deadline := time.Now().Add(time.Duration(*option.Timeout) * time.Millisecond)
var matcher *urlMatcher
if option.URL != nil {
matcher = newURLMatcher(option.URL, f.page.browserContext.options.BaseURL)
}
predicate := func(events ...interface{}) bool {
ev := events[0].(map[string]interface{})
err, ok := ev["error"]
if ok {
// Any failed navigation results in a rejection.
logger.Error("navigation error", "url", ev["url"].(string), "error", err)
return true
}
return matcher == nil || matcher.Matches(ev["url"].(string))
}
waiter, err := f.setNavigationWaiter(option.Timeout)
if err != nil {
return nil, err
}
eventData, err := waiter.WaitForEvent(f, "navigated", predicate).RunAndWait(cb)
if err != nil || eventData == nil {
return nil, err
}
t := time.Until(deadline).Milliseconds()
if t > 0 {
err = f.waitForLoadStateImpl(string(*option.WaitUntil), Float(float64(t)), nil)
if err != nil {
return nil, err
}
}
event := eventData.(map[string]interface{})
if event["newDocument"] != nil && event["newDocument"].(map[string]interface{})["request"] != nil {
request := fromChannel(event["newDocument"].(map[string]interface{})["request"]).(*requestImpl)
return request.Response()
}
return nil, nil
}
func (f *frameImpl) setNavigationWaiter(timeout *float64) (*waiter, error) {
if f.page == nil {
return nil, errors.New("page does not exist")
}
waiter := newWaiter()
if timeout != nil {
waiter.WithTimeout(*timeout)
} else {
waiter.WithTimeout(f.page.timeoutSettings.NavigationTimeout())
}
waiter.RejectOnEvent(f.page, "close", f.page.closeErrorWithReason())
waiter.RejectOnEvent(f.page, "crash", fmt.Errorf("Navigation failed because page crashed!"))
waiter.RejectOnEvent(f.page, "framedetached", fmt.Errorf("Navigating frame was detached!"), func(payload interface{}) bool {
frame, ok := payload.(*frameImpl)
if ok && frame == f {
return true
}
return false
})
return waiter, nil
}
func (f *frameImpl) onFrameNavigated(ev map[string]interface{}) {
f.Lock()
f.url = ev["url"].(string)
f.name = ev["name"].(string)
f.Unlock()
f.Emit("navigated", ev)
_, ok := ev["error"]
if !ok && f.page != nil {
f.page.Emit("framenavigated", f)
}
}
func (f *frameImpl) onLoadState(ev map[string]interface{}) {
if ev["add"] != nil {
add := ev["add"].(string)
f.loadStates.Add(add)
f.Emit("loadstate", add)
if f.parentFrame == nil && f.page != nil {
if add == "load" || add == "domcontentloaded" {
f.Page().Emit(add, f.page)
}
}
} else if ev["remove"] != nil {
remove := ev["remove"].(string)
f.loadStates.Remove(remove)
}
}
func (f *frameImpl) QuerySelector(selector string, options ...FrameQuerySelectorOptions) (ElementHandle, error) {
params := map[string]interface{}{
"selector": selector,
}
if len(options) == 1 {
params["strict"] = options[0].Strict
}
channel, err := f.channel.Send("querySelector", params)
if err != nil {
return nil, err
}
if channel == nil {
return nil, nil
}
return fromChannel(channel).(*elementHandleImpl), nil
}
func (f *frameImpl) QuerySelectorAll(selector string) ([]ElementHandle, error) {
channels, err := f.channel.Send("querySelectorAll", map[string]interface{}{
"selector": selector,
})
if err != nil {
return nil, err
}
elements := make([]ElementHandle, 0)
for _, channel := range channels.([]interface{}) {
elements = append(elements, fromChannel(channel).(*elementHandleImpl))
}
return elements, nil
}
func (f *frameImpl) Evaluate(expression string, options ...interface{}) (interface{}, error) {
var arg interface{}
if len(options) == 1 {
arg = options[0]
}
result, err := f.channel.Send("evaluateExpression", map[string]interface{}{
"expression": expression,
"arg": serializeArgument(arg),
})
if err != nil {
return nil, err
}
return parseResult(result), nil
}
func (f *frameImpl) EvalOnSelector(selector string, expression string, arg interface{}, options ...FrameEvalOnSelectorOptions) (interface{}, error) {
params := map[string]interface{}{
"selector": selector,
"expression": expression,
"arg": serializeArgument(arg),
}
if len(options) == 1 && options[0].Strict != nil {
params["strict"] = *options[0].Strict
}
result, err := f.channel.Send("evalOnSelector", params)
if err != nil {
return nil, err
}
return parseResult(result), nil
}
func (f *frameImpl) EvalOnSelectorAll(selector string, expression string, options ...interface{}) (interface{}, error) {
var arg interface{}
if len(options) == 1 {
arg = options[0]
}
result, err := f.channel.Send("evalOnSelectorAll", map[string]interface{}{
"selector": selector,
"expression": expression,
"arg": serializeArgument(arg),
})
if err != nil {
return nil, err
}
return parseResult(result), nil
}
func (f *frameImpl) EvaluateHandle(expression string, options ...interface{}) (JSHandle, error) {
var arg interface{}
if len(options) == 1 {
arg = options[0]
}
result, err := f.channel.Send("evaluateExpressionHandle", map[string]interface{}{
"expression": expression,
"arg": serializeArgument(arg),
})
if err != nil {
return nil, err
}
channelOwner := fromChannel(result)
if channelOwner == nil {
return nil, nil
}
return channelOwner.(JSHandle), nil
}
func (f *frameImpl) Click(selector string, options ...FrameClickOptions) error {
_, err := f.channel.Send("click", map[string]interface{}{
"selector": selector,
}, options)
return err
}
func (f *frameImpl) WaitForSelector(selector string, options ...FrameWaitForSelectorOptions) (ElementHandle, error) {
channel, err := f.channel.Send("waitForSelector", map[string]interface{}{
"selector": selector,
}, options)
if err != nil {
return nil, err
}
channelOwner := fromNullableChannel(channel)
if channelOwner == nil {
return nil, nil
}
return channelOwner.(*elementHandleImpl), nil
}
func (f *frameImpl) DispatchEvent(selector, typ string, eventInit interface{}, options ...FrameDispatchEventOptions) error {
_, err := f.channel.Send("dispatchEvent", map[string]interface{}{
"selector": selector,
"type": typ,
"eventInit": serializeArgument(eventInit),
})
return err
}
func (f *frameImpl) InnerText(selector string, options ...FrameInnerTextOptions) (string, error) {
innerText, err := f.channel.Send("innerText", map[string]interface{}{
"selector": selector,
}, options)
if innerText == nil {
return "", err
}
return innerText.(string), err
}
func (f *frameImpl) InnerHTML(selector string, options ...FrameInnerHTMLOptions) (string, error) {
innerHTML, err := f.channel.Send("innerHTML", map[string]interface{}{
"selector": selector,
}, options)
if innerHTML == nil {
return "", err
}
return innerHTML.(string), err
}
func (f *frameImpl) GetAttribute(selector string, name string, options ...FrameGetAttributeOptions) (string, error) {
attribute, err := f.channel.Send("getAttribute", map[string]interface{}{
"selector": selector,
"name": name,
}, options)
if attribute == nil {
return "", err
}
return attribute.(string), err
}
func (f *frameImpl) Hover(selector string, options ...FrameHoverOptions) error {
_, err := f.channel.Send("hover", map[string]interface{}{
"selector": selector,
}, options)
return err
}
func (f *frameImpl) SetInputFiles(selector string, files interface{}, options ...FrameSetInputFilesOptions) error {
params, err := convertInputFiles(files, f.page.browserContext)
if err != nil {
return err
}
params.Selector = &selector
_, err = f.channel.Send("setInputFiles", params, options)
return err
}
func (f *frameImpl) Type(selector, text string, options ...FrameTypeOptions) error {
_, err := f.channel.Send("type", map[string]interface{}{
"selector": selector,
"text": text,
}, options)
return err
}
func (f *frameImpl) Press(selector, key string, options ...FramePressOptions) error {
_, err := f.channel.Send("press", map[string]interface{}{
"selector": selector,
"key": key,
}, options)
return err
}
func (f *frameImpl) Check(selector string, options ...FrameCheckOptions) error {
_, err := f.channel.Send("check", map[string]interface{}{
"selector": selector,
}, options)
return err
}
func (f *frameImpl) Uncheck(selector string, options ...FrameUncheckOptions) error {
_, err := f.channel.Send("uncheck", map[string]interface{}{
"selector": selector,
}, options)
return err
}
func (f *frameImpl) WaitForTimeout(timeout float64) {
time.Sleep(time.Duration(timeout) * time.Millisecond)
}
func (f *frameImpl) WaitForFunction(expression string, arg interface{}, options ...FrameWaitForFunctionOptions) (JSHandle, error) {
var option FrameWaitForFunctionOptions
if len(options) == 1 {
option = options[0]
}
result, err := f.channel.Send("waitForFunction", map[string]interface{}{
"expression": expression,
"arg": serializeArgument(arg),
"timeout": option.Timeout,
"polling": option.Polling,
})
if err != nil {
return nil, err
}
handle := fromChannel(result)
if handle == nil {
return nil, nil
}
return handle.(*jsHandleImpl), nil
}
func (f *frameImpl) Title() (string, error) {
title, err := f.channel.Send("title")
if title == nil {
return "", err
}
return title.(string), err
}
func (f *frameImpl) ChildFrames() []Frame {
return f.childFrames
}
func (f *frameImpl) Dblclick(selector string, options ...FrameDblclickOptions) error {
_, err := f.channel.Send("dblclick", map[string]interface{}{
"selector": selector,
}, options)
return err
}
func (f *frameImpl) Fill(selector string, value string, options ...FrameFillOptions) error {
_, err := f.channel.Send("fill", map[string]interface{}{
"selector": selector,
"value": value,
}, options)
return err
}
func (f *frameImpl) Focus(selector string, options ...FrameFocusOptions) error {
_, err := f.channel.Send("focus", map[string]interface{}{
"selector": selector,
}, options)
return err
}
func (f *frameImpl) FrameElement() (ElementHandle, error) {
channel, err := f.channel.Send("frameElement")
if err != nil {
return nil, err
}
return fromChannel(channel).(*elementHandleImpl), nil
}
func (f *frameImpl) IsDetached() bool {
return f.detached
}
func (f *frameImpl) ParentFrame() Frame {
return f.parentFrame
}
func (f *frameImpl) TextContent(selector string, options ...FrameTextContentOptions) (string, error) {
textContent, err := f.channel.Send("textContent", map[string]interface{}{
"selector": selector,
}, options)
if textContent == nil {
return "", err
}
return textContent.(string), err
}
func (f *frameImpl) Tap(selector string, options ...FrameTapOptions) error {
_, err := f.channel.Send("tap", map[string]interface{}{
"selector": selector,
}, options)
return err
}
func (f *frameImpl) SelectOption(selector string, values SelectOptionValues, options ...FrameSelectOptionOptions) ([]string, error) {
opts := convertSelectOptionSet(values)
m := make(map[string]interface{})
m["selector"] = selector
for k, v := range opts {
m[k] = v
}
selected, err := f.channel.Send("selectOption", m, options)
if err != nil {
return nil, err
}
return transformToStringList(selected), nil
}
func (f *frameImpl) IsChecked(selector string, options ...FrameIsCheckedOptions) (bool, error) {
checked, err := f.channel.Send("isChecked", map[string]interface{}{
"selector": selector,
}, options)
if err != nil {
return false, err
}
return checked.(bool), nil
}
func (f *frameImpl) IsDisabled(selector string, options ...FrameIsDisabledOptions) (bool, error) {
disabled, err := f.channel.Send("isDisabled", map[string]interface{}{
"selector": selector,
}, options)
if err != nil {
return false, err
}
return disabled.(bool), nil
}
func (f *frameImpl) IsEditable(selector string, options ...FrameIsEditableOptions) (bool, error) {
editable, err := f.channel.Send("isEditable", map[string]interface{}{
"selector": selector,
}, options)
if err != nil {
return false, err
}
return editable.(bool), nil
}
func (f *frameImpl) IsEnabled(selector string, options ...FrameIsEnabledOptions) (bool, error) {
enabled, err := f.channel.Send("isEnabled", map[string]interface{}{
"selector": selector,
}, options)
if err != nil {
return false, err
}
return enabled.(bool), nil
}
func (f *frameImpl) IsHidden(selector string, options ...FrameIsHiddenOptions) (bool, error) {
hidden, err := f.channel.Send("isHidden", map[string]interface{}{
"selector": selector,
}, options)
if err != nil {
return false, err
}
return hidden.(bool), nil
}
func (f *frameImpl) IsVisible(selector string, options ...FrameIsVisibleOptions) (bool, error) {
visible, err := f.channel.Send("isVisible", map[string]interface{}{
"selector": selector,
}, options)
if err != nil {
return false, err
}
return visible.(bool), nil
}
func (f *frameImpl) InputValue(selector string, options ...FrameInputValueOptions) (string, error) {
value, err := f.channel.Send("inputValue", map[string]interface{}{
"selector": selector,
}, options)
if value == nil {
return "", err
}
return value.(string), err
}
func (f *frameImpl) DragAndDrop(source, target string, options ...FrameDragAndDropOptions) error {
_, err := f.channel.Send("dragAndDrop", map[string]interface{}{
"source": source,
"target": target,
}, options)
return err
}
func (f *frameImpl) SetChecked(selector string, checked bool, options ...FrameSetCheckedOptions) error {
if checked {
_, err := f.channel.Send("check", map[string]interface{}{
"selector": selector,
}, options)
return err
} else {
_, err := f.channel.Send("uncheck", map[string]interface{}{
"selector": selector,
}, options)
return err
}
}
func (f *frameImpl) Locator(selector string, options ...FrameLocatorOptions) Locator {
var option LocatorLocatorOptions
if len(options) == 1 {
option = LocatorLocatorOptions(options[0])
}
return newLocator(f, selector, option)
}
func (f *frameImpl) GetByAltText(text interface{}, options ...FrameGetByAltTextOptions) Locator {
exact := false
if len(options) == 1 {
if *options[0].Exact {
exact = true
}
}
return f.Locator(getByAltTextSelector(text, exact))
}
func (f *frameImpl) GetByLabel(text interface{}, options ...FrameGetByLabelOptions) Locator {
exact := false
if len(options) == 1 {
if *options[0].Exact {
exact = true
}
}
return f.Locator(getByLabelSelector(text, exact))
}
func (f *frameImpl) GetByPlaceholder(text interface{}, options ...FrameGetByPlaceholderOptions) Locator {
exact := false
if len(options) == 1 {
if *options[0].Exact {
exact = true
}
}
return f.Locator(getByPlaceholderSelector(text, exact))
}
func (f *frameImpl) GetByRole(role AriaRole, options ...FrameGetByRoleOptions) Locator {
if len(options) == 1 {
return f.Locator(getByRoleSelector(role, LocatorGetByRoleOptions(options[0])))
}
return f.Locator(getByRoleSelector(role))
}
func (f *frameImpl) GetByTestId(testId interface{}) Locator {
return f.Locator(getByTestIdSelector(getTestIdAttributeName(), testId))
}
func (f *frameImpl) GetByText(text interface{}, options ...FrameGetByTextOptions) Locator {
exact := false
if len(options) == 1 {
if *options[0].Exact {
exact = true
}
}
return f.Locator(getByTextSelector(text, exact))
}
func (f *frameImpl) GetByTitle(text interface{}, options ...FrameGetByTitleOptions) Locator {
exact := false
if len(options) == 1 {
if *options[0].Exact {
exact = true
}
}
return f.Locator(getByTitleSelector(text, exact))
}
func (f *frameImpl) FrameLocator(selector string) FrameLocator {
return newFrameLocator(f, selector)
}
func (f *frameImpl) highlight(selector string) error {
_, err := f.channel.Send("highlight", map[string]interface{}{
"selector": selector,
})
return err
}
func (f *frameImpl) queryCount(selector string) (int, error) {
response, err := f.channel.Send("queryCount", map[string]interface{}{
"selector": selector,
})
if err != nil {
return 0, err
}
return int(response.(float64)), nil
}