1385 lines
38 KiB
Go
1385 lines
38 KiB
Go
package playwright
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"slices"
|
|
"sync"
|
|
|
|
"github.com/playwright-community/playwright-go/internal/safe"
|
|
)
|
|
|
|
type pageImpl struct {
|
|
channelOwner
|
|
isClosed bool
|
|
closedOrCrashed chan error
|
|
video *videoImpl
|
|
mouse *mouseImpl
|
|
keyboard *keyboardImpl
|
|
touchscreen *touchscreenImpl
|
|
timeoutSettings *timeoutSettings
|
|
browserContext *browserContextImpl
|
|
frames []Frame
|
|
workers []Worker
|
|
mainFrame Frame
|
|
routes []*routeHandlerEntry
|
|
webSocketRoutes []*webSocketRouteHandler
|
|
viewportSize *Size
|
|
ownedContext BrowserContext
|
|
bindings *safe.SyncMap[string, BindingCallFunction]
|
|
closeReason *string
|
|
closeWasCalled bool
|
|
harRouters []*harRouter
|
|
locatorHandlers map[float64]*locatorHandlerEntry
|
|
}
|
|
|
|
type locatorHandlerEntry struct {
|
|
locator *locatorImpl
|
|
handler func(Locator)
|
|
times *int
|
|
}
|
|
|
|
func (p *pageImpl) AddLocatorHandler(locator Locator, handler func(Locator), options ...PageAddLocatorHandlerOptions) error {
|
|
if locator == nil || handler == nil {
|
|
return errors.New("locator or handler must not be nil")
|
|
}
|
|
if locator.Err() != nil {
|
|
return locator.Err()
|
|
}
|
|
|
|
var option PageAddLocatorHandlerOptions
|
|
if len(options) == 1 {
|
|
option = options[0]
|
|
if option.Times != nil && *option.Times == 0 {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
loc := locator.(*locatorImpl)
|
|
if loc.frame != p.mainFrame {
|
|
return errors.New("locator must belong to the main frame of this page")
|
|
}
|
|
uid, err := p.channel.Send("registerLocatorHandler", map[string]any{
|
|
"selector": loc.selector,
|
|
"noWaitAfter": option.NoWaitAfter,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.locatorHandlers[uid.(float64)] = &locatorHandlerEntry{locator: loc, handler: handler, times: option.Times}
|
|
return nil
|
|
}
|
|
|
|
func (p *pageImpl) onLocatorHandlerTriggered(uid float64) {
|
|
var remove *bool
|
|
handler, ok := p.locatorHandlers[uid]
|
|
if !ok {
|
|
return
|
|
}
|
|
if handler.times != nil {
|
|
*handler.times--
|
|
if *handler.times == 0 {
|
|
remove = Bool(true)
|
|
}
|
|
}
|
|
defer func() {
|
|
if remove != nil && *remove {
|
|
delete(p.locatorHandlers, uid)
|
|
}
|
|
_, _ = p.connection.WrapAPICall(func() (interface{}, error) {
|
|
_, err := p.channel.Send("resolveLocatorHandlerNoReply", map[string]any{
|
|
"uid": uid,
|
|
"remove": remove,
|
|
})
|
|
return nil, err
|
|
}, true)
|
|
}()
|
|
|
|
handler.handler(handler.locator)
|
|
}
|
|
|
|
func (p *pageImpl) RemoveLocatorHandler(locator Locator) error {
|
|
for uid := range p.locatorHandlers {
|
|
if p.locatorHandlers[uid].locator.equals(locator) {
|
|
delete(p.locatorHandlers, uid)
|
|
p.channel.SendNoReply("unregisterLocatorHandler", map[string]any{
|
|
"uid": uid,
|
|
})
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *pageImpl) Context() BrowserContext {
|
|
return p.browserContext
|
|
}
|
|
|
|
func (b *pageImpl) Clock() Clock {
|
|
return b.browserContext.clock
|
|
}
|
|
|
|
func (p *pageImpl) Close(options ...PageCloseOptions) error {
|
|
if len(options) == 1 {
|
|
p.closeReason = options[0].Reason
|
|
}
|
|
p.closeWasCalled = true
|
|
_, err := p.channel.Send("close", options)
|
|
if err == nil && p.ownedContext != nil {
|
|
err = p.ownedContext.Close()
|
|
}
|
|
if errors.Is(err, ErrTargetClosed) || (len(options) == 1 && options[0].RunBeforeUnload != nil && *(options[0].RunBeforeUnload)) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (p *pageImpl) InnerText(selector string, options ...PageInnerTextOptions) (string, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.InnerText(selector, FrameInnerTextOptions(options[0]))
|
|
}
|
|
return p.mainFrame.InnerText(selector)
|
|
}
|
|
|
|
func (p *pageImpl) InnerHTML(selector string, options ...PageInnerHTMLOptions) (string, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.InnerHTML(selector, FrameInnerHTMLOptions(options[0]))
|
|
}
|
|
return p.mainFrame.InnerHTML(selector)
|
|
}
|
|
|
|
func (p *pageImpl) Opener() (Page, error) {
|
|
channel := p.initializer["opener"]
|
|
channelOwner := fromNullableChannel(channel)
|
|
if channelOwner == nil {
|
|
// not popup page or opener has been closed
|
|
return nil, nil
|
|
}
|
|
return channelOwner.(*pageImpl), nil
|
|
}
|
|
|
|
func (p *pageImpl) MainFrame() Frame {
|
|
return p.mainFrame
|
|
}
|
|
|
|
func (p *pageImpl) Frame(options ...PageFrameOptions) Frame {
|
|
option := PageFrameOptions{}
|
|
if len(options) == 1 {
|
|
option = options[0]
|
|
}
|
|
var matcher *urlMatcher
|
|
if option.URL != nil {
|
|
matcher = newURLMatcher(option.URL, p.browserContext.options.BaseURL)
|
|
}
|
|
|
|
for _, f := range p.frames {
|
|
if option.Name != nil && f.Name() == *option.Name {
|
|
return f
|
|
}
|
|
|
|
if option.URL != nil && matcher != nil && matcher.Matches(f.URL()) {
|
|
return f
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *pageImpl) Frames() []Frame {
|
|
return p.frames
|
|
}
|
|
|
|
func (p *pageImpl) SetDefaultNavigationTimeout(timeout float64) {
|
|
p.timeoutSettings.SetDefaultNavigationTimeout(&timeout)
|
|
p.channel.SendNoReplyInternal("setDefaultNavigationTimeoutNoReply", map[string]interface{}{
|
|
"timeout": timeout,
|
|
})
|
|
}
|
|
|
|
func (p *pageImpl) SetDefaultTimeout(timeout float64) {
|
|
p.timeoutSettings.SetDefaultTimeout(&timeout)
|
|
p.channel.SendNoReplyInternal("setDefaultTimeoutNoReply", map[string]interface{}{
|
|
"timeout": timeout,
|
|
})
|
|
}
|
|
|
|
func (p *pageImpl) QuerySelector(selector string, options ...PageQuerySelectorOptions) (ElementHandle, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.QuerySelector(selector, FrameQuerySelectorOptions(options[0]))
|
|
}
|
|
return p.mainFrame.QuerySelector(selector)
|
|
}
|
|
|
|
func (p *pageImpl) QuerySelectorAll(selector string) ([]ElementHandle, error) {
|
|
return p.mainFrame.QuerySelectorAll(selector)
|
|
}
|
|
|
|
func (p *pageImpl) WaitForSelector(selector string, options ...PageWaitForSelectorOptions) (ElementHandle, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.WaitForSelector(selector, FrameWaitForSelectorOptions(options[0]))
|
|
}
|
|
return p.mainFrame.WaitForSelector(selector)
|
|
}
|
|
|
|
func (p *pageImpl) DispatchEvent(selector string, typ string, eventInit interface{}, options ...PageDispatchEventOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.DispatchEvent(selector, typ, eventInit, FrameDispatchEventOptions(options[0]))
|
|
}
|
|
return p.mainFrame.DispatchEvent(selector, typ, eventInit)
|
|
}
|
|
|
|
func (p *pageImpl) Evaluate(expression string, arg ...interface{}) (interface{}, error) {
|
|
return p.mainFrame.Evaluate(expression, arg...)
|
|
}
|
|
|
|
func (p *pageImpl) EvaluateHandle(expression string, arg ...interface{}) (JSHandle, error) {
|
|
return p.mainFrame.EvaluateHandle(expression, arg...)
|
|
}
|
|
|
|
func (p *pageImpl) EvalOnSelector(selector string, expression string, arg interface{}, options ...PageEvalOnSelectorOptions) (interface{}, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.EvalOnSelector(selector, expression, arg, FrameEvalOnSelectorOptions(options[0]))
|
|
}
|
|
return p.mainFrame.EvalOnSelector(selector, expression, arg)
|
|
}
|
|
|
|
func (p *pageImpl) EvalOnSelectorAll(selector string, expression string, arg ...interface{}) (interface{}, error) {
|
|
return p.mainFrame.EvalOnSelectorAll(selector, expression, arg...)
|
|
}
|
|
|
|
func (p *pageImpl) AddScriptTag(options PageAddScriptTagOptions) (ElementHandle, error) {
|
|
return p.mainFrame.AddScriptTag(FrameAddScriptTagOptions(options))
|
|
}
|
|
|
|
func (p *pageImpl) AddStyleTag(options PageAddStyleTagOptions) (ElementHandle, error) {
|
|
return p.mainFrame.AddStyleTag(FrameAddStyleTagOptions(options))
|
|
}
|
|
|
|
func (p *pageImpl) SetExtraHTTPHeaders(headers map[string]string) error {
|
|
_, err := p.channel.Send("setExtraHTTPHeaders", map[string]interface{}{
|
|
"headers": serializeMapToNameAndValue(headers),
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (p *pageImpl) URL() string {
|
|
return p.mainFrame.URL()
|
|
}
|
|
|
|
func (p *pageImpl) Unroute(url interface{}, handlers ...routeHandler) error {
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
|
|
removed, remaining, err := unroute(p.routes, url, handlers...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return p.unrouteInternal(removed, remaining, UnrouteBehaviorDefault)
|
|
}
|
|
|
|
func (p *pageImpl) unrouteInternal(removed []*routeHandlerEntry, remaining []*routeHandlerEntry, behavior *UnrouteBehavior) error {
|
|
p.routes = remaining
|
|
err := p.updateInterceptionPatterns()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if behavior == nil || behavior == UnrouteBehaviorDefault {
|
|
return nil
|
|
}
|
|
wg := &sync.WaitGroup{}
|
|
for _, entry := range removed {
|
|
wg.Add(1)
|
|
go func(entry *routeHandlerEntry) {
|
|
defer wg.Done()
|
|
entry.Stop(string(*behavior))
|
|
}(entry)
|
|
}
|
|
wg.Wait()
|
|
return nil
|
|
}
|
|
|
|
func (p *pageImpl) disposeHarRouters() {
|
|
for _, router := range p.harRouters {
|
|
router.dispose()
|
|
}
|
|
p.harRouters = make([]*harRouter, 0)
|
|
}
|
|
|
|
func (p *pageImpl) UnrouteAll(options ...PageUnrouteAllOptions) error {
|
|
var behavior *UnrouteBehavior
|
|
if len(options) == 1 {
|
|
behavior = options[0].Behavior
|
|
}
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
defer p.disposeHarRouters()
|
|
return p.unrouteInternal(p.routes, []*routeHandlerEntry{}, behavior)
|
|
}
|
|
|
|
func (p *pageImpl) Content() (string, error) {
|
|
return p.mainFrame.Content()
|
|
}
|
|
|
|
func (p *pageImpl) SetContent(html string, options ...PageSetContentOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.SetContent(html, FrameSetContentOptions(options[0]))
|
|
}
|
|
return p.mainFrame.SetContent(html)
|
|
}
|
|
|
|
func (p *pageImpl) Goto(url string, options ...PageGotoOptions) (Response, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Goto(url, FrameGotoOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Goto(url)
|
|
}
|
|
|
|
func (p *pageImpl) Reload(options ...PageReloadOptions) (Response, error) {
|
|
channel, err := p.channel.Send("reload", options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
channelOwner := fromNullableChannel(channel)
|
|
if channelOwner == nil {
|
|
return nil, nil
|
|
}
|
|
return channelOwner.(*responseImpl), nil
|
|
}
|
|
|
|
func (p *pageImpl) WaitForLoadState(options ...PageWaitForLoadStateOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.WaitForLoadState(FrameWaitForLoadStateOptions(options[0]))
|
|
}
|
|
return p.mainFrame.WaitForLoadState()
|
|
}
|
|
|
|
func (p *pageImpl) GoBack(options ...PageGoBackOptions) (Response, error) {
|
|
channel, err := p.channel.Send("goBack", options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
channelOwner := fromNullableChannel(channel)
|
|
if channelOwner == nil {
|
|
// can not go back
|
|
return nil, nil
|
|
}
|
|
return channelOwner.(*responseImpl), nil
|
|
}
|
|
|
|
func (p *pageImpl) GoForward(options ...PageGoForwardOptions) (Response, error) {
|
|
channel, err := p.channel.Send("goForward", options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
channelOwner := fromNullableChannel(channel)
|
|
if channelOwner == nil {
|
|
// can not go forward
|
|
return nil, nil
|
|
}
|
|
return channelOwner.(*responseImpl), nil
|
|
}
|
|
|
|
func (p *pageImpl) EmulateMedia(options ...PageEmulateMediaOptions) error {
|
|
_, err := p.channel.Send("emulateMedia", options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (p *pageImpl) SetViewportSize(width, height int) error {
|
|
_, err := p.channel.Send("setViewportSize", map[string]interface{}{
|
|
"viewportSize": map[string]interface{}{
|
|
"width": width,
|
|
"height": height,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.viewportSize.Width = width
|
|
p.viewportSize.Height = height
|
|
return nil
|
|
}
|
|
|
|
func (p *pageImpl) ViewportSize() *Size {
|
|
return p.viewportSize
|
|
}
|
|
|
|
func (p *pageImpl) BringToFront() error {
|
|
_, err := p.channel.Send("bringToFront")
|
|
return err
|
|
}
|
|
|
|
func (p *pageImpl) Type(selector, text string, options ...PageTypeOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Type(selector, text, FrameTypeOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Type(selector, text)
|
|
}
|
|
|
|
func (p *pageImpl) Fill(selector, text string, options ...PageFillOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Fill(selector, text, FrameFillOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Fill(selector, text)
|
|
}
|
|
|
|
func (p *pageImpl) Press(selector, key string, options ...PagePressOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Press(selector, key, FramePressOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Press(selector, key)
|
|
}
|
|
|
|
func (p *pageImpl) Title() (string, error) {
|
|
return p.mainFrame.Title()
|
|
}
|
|
|
|
func (p *pageImpl) Workers() []Worker {
|
|
return p.workers
|
|
}
|
|
|
|
func (p *pageImpl) Request() APIRequestContext {
|
|
return p.Context().Request()
|
|
}
|
|
|
|
func (p *pageImpl) Screenshot(options ...PageScreenshotOptions) ([]byte, error) {
|
|
var path *string
|
|
overrides := map[string]interface{}{}
|
|
if len(options) == 1 {
|
|
path = options[0].Path
|
|
options[0].Path = nil
|
|
if options[0].Mask != nil {
|
|
masks := make([]map[string]interface{}, 0)
|
|
for _, m := range options[0].Mask {
|
|
if m.Err() != nil { // ErrLocatorNotSameFrame
|
|
return nil, m.Err()
|
|
}
|
|
l, ok := m.(*locatorImpl)
|
|
if ok {
|
|
masks = append(masks, map[string]interface{}{
|
|
"selector": l.selector,
|
|
"frame": l.frame.channel,
|
|
})
|
|
}
|
|
}
|
|
overrides["mask"] = masks
|
|
}
|
|
}
|
|
data, err := p.channel.Send("screenshot", options, overrides)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
image, err := base64.StdEncoding.DecodeString(data.(string))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not decode base64 :%w", err)
|
|
}
|
|
if path != nil {
|
|
if err := os.WriteFile(*path, image, 0o644); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return image, nil
|
|
}
|
|
|
|
func (p *pageImpl) PDF(options ...PagePdfOptions) ([]byte, error) {
|
|
var path *string
|
|
if len(options) == 1 {
|
|
path = options[0].Path
|
|
}
|
|
data, err := p.channel.Send("pdf", options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pdf, err := base64.StdEncoding.DecodeString(data.(string))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not decode base64 :%w", err)
|
|
}
|
|
if path != nil {
|
|
if err := os.WriteFile(*path, pdf, 0o644); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return pdf, nil
|
|
}
|
|
|
|
func (p *pageImpl) Click(selector string, options ...PageClickOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Click(selector, FrameClickOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Click(selector)
|
|
}
|
|
|
|
func (p *pageImpl) WaitForEvent(event string, options ...PageWaitForEventOptions) (interface{}, error) {
|
|
return p.waiterForEvent(event, options...).Wait()
|
|
}
|
|
|
|
func (p *pageImpl) waiterForEvent(event string, options ...PageWaitForEventOptions) *waiter {
|
|
timeout := p.timeoutSettings.Timeout()
|
|
var predicate interface{} = nil
|
|
if len(options) == 1 {
|
|
if options[0].Timeout != nil {
|
|
timeout = *options[0].Timeout
|
|
}
|
|
predicate = options[0].Predicate
|
|
}
|
|
waiter := newWaiter().WithTimeout(timeout)
|
|
waiter.RejectOnEvent(p, "close", p.closeErrorWithReason())
|
|
waiter.RejectOnEvent(p, "crash", errors.New("page crashed"))
|
|
return waiter.WaitForEvent(p, event, predicate)
|
|
}
|
|
|
|
func (p *pageImpl) waiterForRequest(url interface{}, options ...PageExpectRequestOptions) *waiter {
|
|
option := PageExpectRequestOptions{}
|
|
if len(options) == 1 {
|
|
option = options[0]
|
|
}
|
|
if option.Timeout == nil {
|
|
option.Timeout = Float(p.timeoutSettings.Timeout())
|
|
}
|
|
var matcher *urlMatcher
|
|
if url != nil {
|
|
matcher = newURLMatcher(url, p.browserContext.options.BaseURL)
|
|
}
|
|
predicate := func(req *requestImpl) bool {
|
|
if matcher != nil {
|
|
return matcher.Matches(req.URL())
|
|
}
|
|
return true
|
|
}
|
|
|
|
waiter := newWaiter().WithTimeout(*option.Timeout)
|
|
return waiter.WaitForEvent(p, "request", predicate)
|
|
}
|
|
|
|
func (p *pageImpl) waiterForResponse(url interface{}, options ...PageExpectResponseOptions) *waiter {
|
|
option := PageExpectResponseOptions{}
|
|
if len(options) == 1 {
|
|
option = options[0]
|
|
}
|
|
if option.Timeout == nil {
|
|
option.Timeout = Float(p.timeoutSettings.Timeout())
|
|
}
|
|
var matcher *urlMatcher
|
|
if url != nil {
|
|
matcher = newURLMatcher(url, p.browserContext.options.BaseURL)
|
|
}
|
|
predicate := func(req *responseImpl) bool {
|
|
if matcher != nil {
|
|
return matcher.Matches(req.URL())
|
|
}
|
|
return true
|
|
}
|
|
|
|
waiter := newWaiter().WithTimeout(*option.Timeout)
|
|
return waiter.WaitForEvent(p, "response", predicate)
|
|
}
|
|
|
|
func (p *pageImpl) ExpectEvent(event string, cb func() error, options ...PageExpectEventOptions) (interface{}, error) {
|
|
if len(options) == 1 {
|
|
return p.waiterForEvent(event, PageWaitForEventOptions(options[0])).RunAndWait(cb)
|
|
}
|
|
return p.waiterForEvent(event).RunAndWait(cb)
|
|
}
|
|
|
|
func (p *pageImpl) ExpectNavigation(cb func() error, options ...PageExpectNavigationOptions) (Response, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.ExpectNavigation(cb, FrameExpectNavigationOptions(options[0]))
|
|
}
|
|
return p.mainFrame.ExpectNavigation(cb)
|
|
}
|
|
|
|
func (p *pageImpl) ExpectConsoleMessage(cb func() error, options ...PageExpectConsoleMessageOptions) (ConsoleMessage, error) {
|
|
option := PageWaitForEventOptions{}
|
|
if len(options) == 1 {
|
|
option.Timeout = options[0].Timeout
|
|
option.Predicate = options[0].Predicate
|
|
}
|
|
ret, err := p.waiterForEvent("console", option).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*consoleMessageImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectDownload(cb func() error, options ...PageExpectDownloadOptions) (Download, error) {
|
|
option := PageWaitForEventOptions{}
|
|
if len(options) == 1 {
|
|
option.Timeout = options[0].Timeout
|
|
option.Predicate = options[0].Predicate
|
|
}
|
|
ret, err := p.waiterForEvent("download", option).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*downloadImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectFileChooser(cb func() error, options ...PageExpectFileChooserOptions) (FileChooser, error) {
|
|
option := PageWaitForEventOptions{}
|
|
if len(options) == 1 {
|
|
option.Timeout = options[0].Timeout
|
|
option.Predicate = options[0].Predicate
|
|
}
|
|
ret, err := p.waiterForEvent("filechooser", option).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*fileChooserImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectPopup(cb func() error, options ...PageExpectPopupOptions) (Page, error) {
|
|
option := PageWaitForEventOptions{}
|
|
if len(options) == 1 {
|
|
option.Timeout = options[0].Timeout
|
|
option.Predicate = options[0].Predicate
|
|
}
|
|
ret, err := p.waiterForEvent("popup", option).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*pageImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectResponse(url interface{}, cb func() error, options ...PageExpectResponseOptions) (Response, error) {
|
|
ret, err := p.waiterForResponse(url, options...).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*responseImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectRequest(url interface{}, cb func() error, options ...PageExpectRequestOptions) (Request, error) {
|
|
ret, err := p.waiterForRequest(url, options...).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*requestImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectRequestFinished(cb func() error, options ...PageExpectRequestFinishedOptions) (Request, error) {
|
|
option := PageWaitForEventOptions{}
|
|
if len(options) == 1 {
|
|
option.Timeout = options[0].Timeout
|
|
option.Predicate = options[0].Predicate
|
|
}
|
|
ret, err := p.waiterForEvent("requestfinished", option).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*requestImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectWebSocket(cb func() error, options ...PageExpectWebSocketOptions) (WebSocket, error) {
|
|
option := PageWaitForEventOptions{}
|
|
if len(options) == 1 {
|
|
option.Timeout = options[0].Timeout
|
|
option.Predicate = options[0].Predicate
|
|
}
|
|
ret, err := p.waiterForEvent("websocket", option).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*webSocketImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) ExpectWorker(cb func() error, options ...PageExpectWorkerOptions) (Worker, error) {
|
|
option := PageWaitForEventOptions{}
|
|
if len(options) == 1 {
|
|
option.Timeout = options[0].Timeout
|
|
option.Predicate = options[0].Predicate
|
|
}
|
|
ret, err := p.waiterForEvent("worker", option).RunAndWait(cb)
|
|
if ret == nil {
|
|
return nil, err
|
|
}
|
|
return ret.(*workerImpl), err
|
|
}
|
|
|
|
func (p *pageImpl) Route(url interface{}, handler routeHandler, times ...int) error {
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
p.routes = slices.Insert(p.routes, 0, newRouteHandlerEntry(newURLMatcher(url, p.browserContext.options.BaseURL), handler, times...))
|
|
return p.updateInterceptionPatterns()
|
|
}
|
|
|
|
func (p *pageImpl) GetAttribute(selector string, name string, options ...PageGetAttributeOptions) (string, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.GetAttribute(selector, name, FrameGetAttributeOptions(options[0]))
|
|
}
|
|
return p.mainFrame.GetAttribute(selector, name)
|
|
}
|
|
|
|
func (p *pageImpl) Hover(selector string, options ...PageHoverOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Hover(selector, FrameHoverOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Hover(selector)
|
|
}
|
|
|
|
func (p *pageImpl) IsClosed() bool {
|
|
return p.isClosed
|
|
}
|
|
|
|
func (p *pageImpl) AddInitScript(script Script) error {
|
|
var source string
|
|
if script.Content != nil {
|
|
source = *script.Content
|
|
}
|
|
if script.Path != nil {
|
|
content, err := os.ReadFile(*script.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
source = string(content)
|
|
}
|
|
_, err := p.channel.Send("addInitScript", map[string]interface{}{
|
|
"source": source,
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (p *pageImpl) Keyboard() Keyboard {
|
|
return p.keyboard
|
|
}
|
|
|
|
func (p *pageImpl) Mouse() Mouse {
|
|
return p.mouse
|
|
}
|
|
|
|
func (p *pageImpl) RouteFromHAR(har string, options ...PageRouteFromHAROptions) error {
|
|
opt := PageRouteFromHAROptions{}
|
|
if len(options) == 1 {
|
|
opt = options[0]
|
|
}
|
|
if opt.Update != nil && *opt.Update {
|
|
return p.browserContext.recordIntoHar(har, browserContextRecordIntoHarOptions{
|
|
Page: p,
|
|
URL: opt.URL,
|
|
})
|
|
}
|
|
notFound := opt.NotFound
|
|
if notFound == nil {
|
|
notFound = HarNotFoundAbort
|
|
}
|
|
router := newHarRouter(p.connection.localUtils, har, *notFound, opt.URL)
|
|
p.harRouters = append(p.harRouters, router)
|
|
return router.addPageRoute(p)
|
|
}
|
|
|
|
func (p *pageImpl) Touchscreen() Touchscreen {
|
|
return p.touchscreen
|
|
}
|
|
|
|
func newPage(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *pageImpl {
|
|
viewportSize := &Size{}
|
|
if _, ok := initializer["viewportSize"].(map[string]interface{}); ok {
|
|
viewportSize.Height = int(initializer["viewportSize"].(map[string]interface{})["height"].(float64))
|
|
viewportSize.Width = int(initializer["viewportSize"].(map[string]interface{})["width"].(float64))
|
|
}
|
|
bt := &pageImpl{
|
|
workers: make([]Worker, 0),
|
|
routes: make([]*routeHandlerEntry, 0),
|
|
bindings: safe.NewSyncMap[string, BindingCallFunction](),
|
|
viewportSize: viewportSize,
|
|
harRouters: make([]*harRouter, 0),
|
|
locatorHandlers: make(map[float64]*locatorHandlerEntry, 0),
|
|
}
|
|
bt.createChannelOwner(bt, parent, objectType, guid, initializer)
|
|
bt.browserContext = fromChannel(parent.channel).(*browserContextImpl)
|
|
bt.timeoutSettings = newTimeoutSettings(bt.browserContext.timeoutSettings)
|
|
mainframe := fromChannel(initializer["mainFrame"]).(*frameImpl)
|
|
mainframe.page = bt
|
|
bt.mainFrame = mainframe
|
|
bt.frames = []Frame{mainframe}
|
|
bt.mouse = newMouse(bt.channel)
|
|
bt.keyboard = newKeyboard(bt.channel)
|
|
bt.touchscreen = newTouchscreen(bt.channel)
|
|
bt.channel.On("bindingCall", func(params map[string]interface{}) {
|
|
bt.onBinding(fromChannel(params["binding"]).(*bindingCallImpl))
|
|
})
|
|
bt.channel.On("close", bt.onClose)
|
|
bt.channel.On("crash", func() {
|
|
bt.Emit("crash", bt)
|
|
})
|
|
bt.channel.On("domcontentloaded", func() {
|
|
bt.Emit("domcontentloaded", bt)
|
|
})
|
|
bt.channel.On("fileChooser", func(ev map[string]interface{}) {
|
|
bt.Emit("filechooser", newFileChooser(bt, fromChannel(ev["element"]).(*elementHandleImpl), ev["isMultiple"].(bool)))
|
|
})
|
|
bt.channel.On("frameAttached", func(ev map[string]interface{}) {
|
|
bt.onFrameAttached(fromChannel(ev["frame"]).(*frameImpl))
|
|
})
|
|
bt.channel.On("frameDetached", func(ev map[string]interface{}) {
|
|
bt.onFrameDetached(fromChannel(ev["frame"]).(*frameImpl))
|
|
})
|
|
bt.channel.On("locatorHandlerTriggered", func(ev map[string]interface{}) {
|
|
bt.channel.CreateTask(func() {
|
|
bt.onLocatorHandlerTriggered(ev["uid"].(float64))
|
|
})
|
|
})
|
|
bt.channel.On(
|
|
"load", func(ev map[string]interface{}) {
|
|
bt.Emit("load", bt)
|
|
},
|
|
)
|
|
bt.channel.On("popup", func(ev map[string]interface{}) {
|
|
bt.Emit("popup", fromChannel(ev["page"]).(*pageImpl))
|
|
})
|
|
bt.channel.On("route", func(ev map[string]interface{}) {
|
|
bt.channel.CreateTask(func() {
|
|
bt.onRoute(fromChannel(ev["route"]).(*routeImpl))
|
|
})
|
|
})
|
|
bt.channel.On("download", func(ev map[string]interface{}) {
|
|
url := ev["url"].(string)
|
|
suggestedFilename := ev["suggestedFilename"].(string)
|
|
artifact := fromChannel(ev["artifact"]).(*artifactImpl)
|
|
bt.Emit("download", newDownload(bt, url, suggestedFilename, artifact))
|
|
})
|
|
bt.channel.On("video", func(params map[string]interface{}) {
|
|
artifact := fromChannel(params["artifact"]).(*artifactImpl)
|
|
bt.Video().(*videoImpl).artifactReady(artifact)
|
|
})
|
|
bt.channel.On("webSocket", func(ev map[string]interface{}) {
|
|
bt.Emit("websocket", fromChannel(ev["webSocket"]).(*webSocketImpl))
|
|
})
|
|
bt.channel.On("webSocketRoute", func(ev map[string]interface{}) {
|
|
bt.channel.CreateTask(func() {
|
|
bt.onWebSocketRoute(fromChannel(ev["webSocketRoute"]).(*webSocketRouteImpl))
|
|
})
|
|
})
|
|
|
|
bt.channel.On("worker", func(ev map[string]interface{}) {
|
|
bt.onWorker(fromChannel(ev["worker"]).(*workerImpl))
|
|
})
|
|
bt.closedOrCrashed = make(chan error, 1)
|
|
bt.OnClose(func(Page) {
|
|
select {
|
|
case bt.closedOrCrashed <- bt.closeErrorWithReason():
|
|
default:
|
|
}
|
|
})
|
|
bt.OnCrash(func(Page) {
|
|
select {
|
|
case bt.closedOrCrashed <- ErrTargetClosed:
|
|
default:
|
|
}
|
|
})
|
|
bt.setEventSubscriptionMapping(map[string]string{
|
|
"console": "console",
|
|
"dialog": "dialog",
|
|
"request": "request",
|
|
"response": "response",
|
|
"requestfinished": "requestFinished",
|
|
"responsefailed": "responseFailed",
|
|
"filechooser": "fileChooser",
|
|
})
|
|
|
|
return bt
|
|
}
|
|
|
|
func (p *pageImpl) closeErrorWithReason() error {
|
|
if p.closeReason != nil {
|
|
return targetClosedError(p.closeReason)
|
|
}
|
|
return targetClosedError(p.browserContext.effectiveCloseReason())
|
|
}
|
|
|
|
func (p *pageImpl) onBinding(binding *bindingCallImpl) {
|
|
function, ok := p.bindings.Load(binding.initializer["name"].(string))
|
|
if !ok || function == nil {
|
|
return
|
|
}
|
|
go binding.Call(function)
|
|
}
|
|
|
|
func (p *pageImpl) onFrameAttached(frame *frameImpl) {
|
|
frame.page = p
|
|
p.frames = append(p.frames, frame)
|
|
p.Emit("frameattached", frame)
|
|
}
|
|
|
|
func (p *pageImpl) onFrameDetached(frame *frameImpl) {
|
|
frame.detached = true
|
|
frames := make([]Frame, 0)
|
|
for i := 0; i < len(p.frames); i++ {
|
|
if p.frames[i] != frame {
|
|
frames = append(frames, frame)
|
|
}
|
|
}
|
|
if len(frames) != len(p.frames) {
|
|
p.frames = frames
|
|
}
|
|
p.Emit("framedetached", frame)
|
|
}
|
|
|
|
func (p *pageImpl) onRoute(route *routeImpl) {
|
|
p.Lock()
|
|
route.context = p.browserContext
|
|
routes := make([]*routeHandlerEntry, len(p.routes))
|
|
copy(routes, p.routes)
|
|
p.Unlock()
|
|
|
|
checkInterceptionIfNeeded := func() {
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
if len(p.routes) == 0 {
|
|
_, err := p.connection.WrapAPICall(func() (interface{}, error) {
|
|
err := p.updateInterceptionPatterns()
|
|
return nil, err
|
|
}, true)
|
|
if err != nil {
|
|
logger.Error("could not update interception patterns", "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
url := route.Request().URL()
|
|
for _, handlerEntry := range routes {
|
|
// If the page was closed we stall all requests right away.
|
|
if p.closeWasCalled || p.browserContext.closeWasCalled {
|
|
return
|
|
}
|
|
if !handlerEntry.Matches(url) {
|
|
continue
|
|
}
|
|
if !slices.ContainsFunc(p.routes, func(entry *routeHandlerEntry) bool {
|
|
return entry == handlerEntry
|
|
}) {
|
|
continue
|
|
}
|
|
if handlerEntry.WillExceed() {
|
|
p.routes = slices.DeleteFunc(p.routes, func(rhe *routeHandlerEntry) bool {
|
|
return rhe == handlerEntry
|
|
})
|
|
}
|
|
handled := handlerEntry.Handle(route)
|
|
checkInterceptionIfNeeded()
|
|
|
|
if <-handled {
|
|
return
|
|
}
|
|
}
|
|
p.browserContext.onRoute(route)
|
|
}
|
|
|
|
func (p *pageImpl) updateInterceptionPatterns() error {
|
|
patterns := prepareInterceptionPatterns(p.routes)
|
|
_, err := p.channel.Send("setNetworkInterceptionPatterns", map[string]interface{}{
|
|
"patterns": patterns,
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (p *pageImpl) onWorker(worker *workerImpl) {
|
|
p.workers = append(p.workers, worker)
|
|
worker.page = p
|
|
p.Emit("worker", worker)
|
|
}
|
|
|
|
func (p *pageImpl) onClose() {
|
|
p.isClosed = true
|
|
newPages := []Page{}
|
|
newBackgoundPages := []Page{}
|
|
if p.browserContext != nil {
|
|
p.browserContext.Lock()
|
|
for _, page := range p.browserContext.pages {
|
|
if page != p {
|
|
newPages = append(newPages, page)
|
|
}
|
|
}
|
|
for _, page := range p.browserContext.backgroundPages {
|
|
if page != p {
|
|
newBackgoundPages = append(newBackgoundPages, page)
|
|
}
|
|
}
|
|
p.browserContext.pages = newPages
|
|
p.browserContext.backgroundPages = newBackgoundPages
|
|
p.browserContext.Unlock()
|
|
}
|
|
p.disposeHarRouters()
|
|
p.Emit("close", p)
|
|
}
|
|
|
|
func (p *pageImpl) SetInputFiles(selector string, files interface{}, options ...PageSetInputFilesOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.SetInputFiles(selector, files, FrameSetInputFilesOptions(options[0]))
|
|
}
|
|
return p.mainFrame.SetInputFiles(selector, files)
|
|
}
|
|
|
|
func (p *pageImpl) Check(selector string, options ...PageCheckOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Check(selector, FrameCheckOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Check(selector)
|
|
}
|
|
|
|
func (p *pageImpl) Uncheck(selector string, options ...PageUncheckOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Uncheck(selector, FrameUncheckOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Uncheck(selector)
|
|
}
|
|
|
|
func (p *pageImpl) WaitForTimeout(timeout float64) {
|
|
p.mainFrame.WaitForTimeout(timeout)
|
|
}
|
|
|
|
func (p *pageImpl) WaitForFunction(expression string, arg interface{}, options ...PageWaitForFunctionOptions) (JSHandle, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.WaitForFunction(expression, arg, FrameWaitForFunctionOptions(options[0]))
|
|
}
|
|
return p.mainFrame.WaitForFunction(expression, arg)
|
|
}
|
|
|
|
func (p *pageImpl) Dblclick(expression string, options ...PageDblclickOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Dblclick(expression, FrameDblclickOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Dblclick(expression)
|
|
}
|
|
|
|
func (p *pageImpl) Focus(expression string, options ...PageFocusOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Focus(expression, FrameFocusOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Focus(expression)
|
|
}
|
|
|
|
func (p *pageImpl) TextContent(selector string, options ...PageTextContentOptions) (string, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.TextContent(selector, FrameTextContentOptions(options[0]))
|
|
}
|
|
return p.mainFrame.TextContent(selector)
|
|
}
|
|
|
|
func (p *pageImpl) Video() Video {
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
|
|
if p.video == nil {
|
|
p.video = newVideo(p)
|
|
}
|
|
return p.video
|
|
}
|
|
|
|
func (p *pageImpl) Tap(selector string, options ...PageTapOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.Tap(selector, FrameTapOptions(options[0]))
|
|
}
|
|
return p.mainFrame.Tap(selector)
|
|
}
|
|
|
|
func (p *pageImpl) ExposeFunction(name string, binding ExposedFunction) error {
|
|
return p.ExposeBinding(name, func(source *BindingSource, args ...interface{}) interface{} {
|
|
return binding(args...)
|
|
})
|
|
}
|
|
|
|
func (p *pageImpl) ExposeBinding(name string, binding BindingCallFunction, handle ...bool) error {
|
|
needsHandle := false
|
|
if len(handle) == 1 {
|
|
needsHandle = handle[0]
|
|
}
|
|
if _, ok := p.bindings.Load(name); ok {
|
|
return fmt.Errorf("Function '%s' has been already registered", name)
|
|
}
|
|
if _, ok := p.browserContext.bindings.Load(name); ok {
|
|
return fmt.Errorf("Function '%s' has been already registered in the browser context", name)
|
|
}
|
|
_, err := p.channel.Send("exposeBinding", map[string]interface{}{
|
|
"name": name,
|
|
"needsHandle": needsHandle,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.bindings.Store(name, binding)
|
|
return nil
|
|
}
|
|
|
|
func (p *pageImpl) SelectOption(selector string, values SelectOptionValues, options ...PageSelectOptionOptions) ([]string, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.SelectOption(selector, values, FrameSelectOptionOptions(options[0]))
|
|
}
|
|
return p.mainFrame.SelectOption(selector, values)
|
|
}
|
|
|
|
func (p *pageImpl) IsChecked(selector string, options ...PageIsCheckedOptions) (bool, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.IsChecked(selector, FrameIsCheckedOptions(options[0]))
|
|
}
|
|
return p.mainFrame.IsChecked(selector)
|
|
}
|
|
|
|
func (p *pageImpl) IsDisabled(selector string, options ...PageIsDisabledOptions) (bool, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.IsDisabled(selector, FrameIsDisabledOptions(options[0]))
|
|
}
|
|
return p.mainFrame.IsDisabled(selector)
|
|
}
|
|
|
|
func (p *pageImpl) IsEditable(selector string, options ...PageIsEditableOptions) (bool, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.IsEditable(selector, FrameIsEditableOptions(options[0]))
|
|
}
|
|
return p.mainFrame.IsEditable(selector)
|
|
}
|
|
|
|
func (p *pageImpl) IsEnabled(selector string, options ...PageIsEnabledOptions) (bool, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.IsEnabled(selector, FrameIsEnabledOptions(options[0]))
|
|
}
|
|
return p.mainFrame.IsEnabled(selector)
|
|
}
|
|
|
|
func (p *pageImpl) IsHidden(selector string, options ...PageIsHiddenOptions) (bool, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.IsHidden(selector, FrameIsHiddenOptions(options[0]))
|
|
}
|
|
return p.mainFrame.IsHidden(selector)
|
|
}
|
|
|
|
func (p *pageImpl) IsVisible(selector string, options ...PageIsVisibleOptions) (bool, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.IsVisible(selector, FrameIsVisibleOptions(options[0]))
|
|
}
|
|
return p.mainFrame.IsVisible(selector)
|
|
}
|
|
|
|
func (p *pageImpl) DragAndDrop(source, target string, options ...PageDragAndDropOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.DragAndDrop(source, target, FrameDragAndDropOptions(options[0]))
|
|
}
|
|
return p.mainFrame.DragAndDrop(source, target)
|
|
}
|
|
|
|
func (p *pageImpl) Pause() (err error) {
|
|
defaultNavigationTimout := p.browserContext.timeoutSettings.DefaultNavigationTimeout()
|
|
defaultTimeout := p.browserContext.timeoutSettings.DefaultTimeout()
|
|
p.browserContext.SetDefaultNavigationTimeout(0)
|
|
p.browserContext.SetDefaultTimeout(0)
|
|
select {
|
|
case err = <-p.closedOrCrashed:
|
|
case err = <-p.browserContext.pause():
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.browserContext.setDefaultNavigationTimeoutImpl(defaultNavigationTimout)
|
|
p.browserContext.setDefaultTimeoutImpl(defaultTimeout)
|
|
return
|
|
}
|
|
|
|
func (p *pageImpl) InputValue(selector string, options ...PageInputValueOptions) (string, error) {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.InputValue(selector, FrameInputValueOptions(options[0]))
|
|
}
|
|
return p.mainFrame.InputValue(selector)
|
|
}
|
|
|
|
func (p *pageImpl) WaitForURL(url interface{}, options ...PageWaitForURLOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.WaitForURL(url, FrameWaitForURLOptions(options[0]))
|
|
}
|
|
return p.mainFrame.WaitForURL(url)
|
|
}
|
|
|
|
func (p *pageImpl) SetChecked(selector string, checked bool, options ...PageSetCheckedOptions) error {
|
|
if len(options) == 1 {
|
|
return p.mainFrame.SetChecked(selector, checked, FrameSetCheckedOptions(options[0]))
|
|
}
|
|
return p.mainFrame.SetChecked(selector, checked)
|
|
}
|
|
|
|
func (p *pageImpl) Locator(selector string, options ...PageLocatorOptions) Locator {
|
|
var option FrameLocatorOptions
|
|
if len(options) == 1 {
|
|
option = FrameLocatorOptions(options[0])
|
|
}
|
|
return p.mainFrame.Locator(selector, option)
|
|
}
|
|
|
|
func (p *pageImpl) GetByAltText(text interface{}, options ...PageGetByAltTextOptions) Locator {
|
|
exact := false
|
|
if len(options) == 1 {
|
|
if *options[0].Exact {
|
|
exact = true
|
|
}
|
|
}
|
|
return p.Locator(getByAltTextSelector(text, exact))
|
|
}
|
|
|
|
func (p *pageImpl) GetByLabel(text interface{}, options ...PageGetByLabelOptions) Locator {
|
|
exact := false
|
|
if len(options) == 1 {
|
|
if *options[0].Exact {
|
|
exact = true
|
|
}
|
|
}
|
|
return p.Locator(getByLabelSelector(text, exact))
|
|
}
|
|
|
|
func (p *pageImpl) GetByPlaceholder(text interface{}, options ...PageGetByPlaceholderOptions) Locator {
|
|
exact := false
|
|
if len(options) == 1 {
|
|
if *options[0].Exact {
|
|
exact = true
|
|
}
|
|
}
|
|
return p.Locator(getByPlaceholderSelector(text, exact))
|
|
}
|
|
|
|
func (p *pageImpl) GetByRole(role AriaRole, options ...PageGetByRoleOptions) Locator {
|
|
if len(options) == 1 {
|
|
return p.Locator(getByRoleSelector(role, LocatorGetByRoleOptions(options[0])))
|
|
}
|
|
return p.Locator(getByRoleSelector(role))
|
|
}
|
|
|
|
func (p *pageImpl) GetByTestId(testId interface{}) Locator {
|
|
return p.Locator(getByTestIdSelector(getTestIdAttributeName(), testId))
|
|
}
|
|
|
|
func (p *pageImpl) GetByText(text interface{}, options ...PageGetByTextOptions) Locator {
|
|
exact := false
|
|
if len(options) == 1 {
|
|
if *options[0].Exact {
|
|
exact = true
|
|
}
|
|
}
|
|
return p.Locator(getByTextSelector(text, exact))
|
|
}
|
|
|
|
func (p *pageImpl) GetByTitle(text interface{}, options ...PageGetByTitleOptions) Locator {
|
|
exact := false
|
|
if len(options) == 1 {
|
|
if *options[0].Exact {
|
|
exact = true
|
|
}
|
|
}
|
|
return p.Locator(getByTitleSelector(text, exact))
|
|
}
|
|
|
|
func (p *pageImpl) FrameLocator(selector string) FrameLocator {
|
|
return p.mainFrame.FrameLocator(selector)
|
|
}
|
|
|
|
func (p *pageImpl) OnClose(fn func(Page)) {
|
|
p.On("close", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnConsole(fn func(ConsoleMessage)) {
|
|
p.On("console", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnCrash(fn func(Page)) {
|
|
p.On("crash", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnDialog(fn func(Dialog)) {
|
|
p.On("dialog", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnDOMContentLoaded(fn func(Page)) {
|
|
p.On("domcontentloaded", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnDownload(fn func(Download)) {
|
|
p.On("download", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnFileChooser(fn func(FileChooser)) {
|
|
p.On("filechooser", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnFrameAttached(fn func(Frame)) {
|
|
p.On("frameattached", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnFrameDetached(fn func(Frame)) {
|
|
p.On("framedetached", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnFrameNavigated(fn func(Frame)) {
|
|
p.On("framenavigated", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnLoad(fn func(Page)) {
|
|
p.On("load", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnPageError(fn func(error)) {
|
|
p.On("pageerror", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnPopup(fn func(Page)) {
|
|
p.On("popup", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnRequest(fn func(Request)) {
|
|
p.On("request", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnRequestFailed(fn func(Request)) {
|
|
p.On("requestfailed", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnRequestFinished(fn func(Request)) {
|
|
p.On("requestfinished", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnResponse(fn func(Response)) {
|
|
p.On("response", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnWebSocket(fn func(WebSocket)) {
|
|
p.On("websocket", fn)
|
|
}
|
|
|
|
func (p *pageImpl) OnWorker(fn func(Worker)) {
|
|
p.On("worker", fn)
|
|
}
|
|
|
|
func (p *pageImpl) RequestGC() error {
|
|
_, err := p.channel.Send("requestGC")
|
|
return err
|
|
}
|
|
|
|
func (p *pageImpl) RouteWebSocket(url interface{}, handler func(WebSocketRoute)) error {
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
p.webSocketRoutes = slices.Insert(p.webSocketRoutes, 0, newWebSocketRouteHandler(newURLMatcher(url, p.browserContext.options.BaseURL), handler))
|
|
|
|
return p.updateWebSocketInterceptionPatterns()
|
|
}
|
|
|
|
func (p *pageImpl) onWebSocketRoute(wr WebSocketRoute) {
|
|
p.Lock()
|
|
index := slices.IndexFunc(p.webSocketRoutes, func(r *webSocketRouteHandler) bool {
|
|
return r.Matches(wr.URL())
|
|
})
|
|
if index == -1 {
|
|
p.Unlock()
|
|
p.browserContext.onWebSocketRoute(wr)
|
|
return
|
|
}
|
|
handler := p.webSocketRoutes[index]
|
|
p.Unlock()
|
|
handler.Handle(wr)
|
|
}
|
|
|
|
func (p *pageImpl) updateWebSocketInterceptionPatterns() error {
|
|
patterns := prepareWebSocketRouteHandlerInterceptionPatterns(p.webSocketRoutes)
|
|
_, err := p.channel.Send("setWebSocketInterceptionPatterns", map[string]interface{}{
|
|
"patterns": patterns,
|
|
})
|
|
return err
|
|
}
|