...

Source file src/github.com/playwright-community/playwright-go/browser_context.go

Documentation: github.com/playwright-community/playwright-go

     1  package playwright
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  )
    11  
    12  type browserContextImpl struct {
    13  	channelOwner
    14  	timeoutSettings   *timeoutSettings
    15  	isClosedOrClosing bool
    16  	options           *BrowserNewContextOptions
    17  	pages             []Page
    18  	routes            []*routeHandlerEntry
    19  	ownedPage         Page
    20  	browser           *browserImpl
    21  	serviceWorkers    []*workerImpl
    22  	backgroundPages   []Page
    23  	bindings          map[string]BindingCallFunction
    24  	tracing           *tracingImpl
    25  }
    26  
    27  func (b *browserContextImpl) SetDefaultNavigationTimeout(timeout float64) {
    28  	b.timeoutSettings.SetNavigationTimeout(timeout)
    29  	b.channel.SendNoReply("setDefaultNavigationTimeoutNoReply", map[string]interface{}{
    30  		"timeout": timeout,
    31  	})
    32  }
    33  
    34  func (b *browserContextImpl) SetDefaultTimeout(timeout float64) {
    35  	b.timeoutSettings.SetTimeout(timeout)
    36  	b.channel.SendNoReply("setDefaultTimeoutNoReply", map[string]interface{}{
    37  		"timeout": timeout,
    38  	})
    39  }
    40  
    41  func (b *browserContextImpl) Pages() []Page {
    42  	b.Lock()
    43  	defer b.Unlock()
    44  	return b.pages
    45  }
    46  
    47  func (b *browserContextImpl) Browser() Browser {
    48  	return b.browser
    49  }
    50  func (b *browserContextImpl) Tracing() Tracing {
    51  	return b.tracing
    52  }
    53  
    54  func (b *browserContextImpl) NewCDPSession(page Page) (CDPSession, error) {
    55  	channel, err := b.channel.Send("newCDPSession", map[string]interface{}{
    56  		"page": page.(*pageImpl).channel,
    57  	})
    58  	if err != nil {
    59  		return nil, fmt.Errorf("could not send message: %w", err)
    60  	}
    61  
    62  	cdpSession := fromChannel(channel).(*cdpSessionImpl)
    63  
    64  	return cdpSession, nil
    65  }
    66  
    67  func (b *browserContextImpl) NewPage(options ...BrowserNewPageOptions) (Page, error) {
    68  	if b.ownedPage != nil {
    69  		return nil, errors.New("Please use browser.NewContext()")
    70  	}
    71  	channel, err := b.channel.Send("newPage", options)
    72  	if err != nil {
    73  		return nil, fmt.Errorf("could not send message: %w", err)
    74  	}
    75  	return fromChannel(channel).(*pageImpl), nil
    76  }
    77  
    78  func (b *browserContextImpl) Cookies(urls ...string) ([]*BrowserContextCookiesResult, error) {
    79  	result, err := b.channel.Send("cookies", map[string]interface{}{
    80  		"urls": urls,
    81  	})
    82  	if err != nil {
    83  		return nil, fmt.Errorf("could not send message: %w", err)
    84  	}
    85  	cookies := make([]*BrowserContextCookiesResult, len(result.([]interface{})))
    86  	for i, cookie := range result.([]interface{}) {
    87  		cookies[i] = &BrowserContextCookiesResult{}
    88  		remapMapToStruct(cookie, cookies[i])
    89  	}
    90  	return cookies, nil
    91  }
    92  
    93  func (b *browserContextImpl) AddCookies(cookies ...BrowserContextAddCookiesOptionsCookies) error {
    94  	_, err := b.channel.Send("addCookies", map[string]interface{}{
    95  		"cookies": cookies,
    96  	})
    97  	return err
    98  }
    99  
   100  func (b *browserContextImpl) ClearCookies() error {
   101  	_, err := b.channel.Send("clearCookies")
   102  	return err
   103  }
   104  
   105  func (b *browserContextImpl) GrantPermissions(permissions []string, options ...BrowserContextGrantPermissionsOptions) error {
   106  	_, err := b.channel.Send("grantPermissions", map[string]interface{}{
   107  		"permissions": permissions,
   108  	}, options)
   109  	return err
   110  }
   111  
   112  func (b *browserContextImpl) ClearPermissions() error {
   113  	_, err := b.channel.Send("clearPermissions")
   114  	return err
   115  }
   116  
   117  // SetGeolocationOptions represents the options for BrowserContext.SetGeolocation()
   118  type SetGeolocationOptions struct {
   119  	Longitude int  `json:"longitude"`
   120  	Latitude  int  `json:"latitude"`
   121  	Accuracy  *int `json:"accuracy"`
   122  }
   123  
   124  func (b *browserContextImpl) SetGeolocation(gelocation *SetGeolocationOptions) error {
   125  	_, err := b.channel.Send("setGeolocation", map[string]interface{}{
   126  		"geolocation": gelocation,
   127  	})
   128  	return err
   129  }
   130  
   131  func (b *browserContextImpl) ResetGeolocation() error {
   132  	_, err := b.channel.Send("setGeolocation", map[string]interface{}{})
   133  	return err
   134  }
   135  
   136  func (b *browserContextImpl) SetExtraHTTPHeaders(headers map[string]string) error {
   137  	_, err := b.channel.Send("setExtraHTTPHeaders", map[string]interface{}{
   138  		"headers": serializeMapToNameAndValue(headers),
   139  	})
   140  	return err
   141  }
   142  
   143  func (b *browserContextImpl) SetOffline(offline bool) error {
   144  	_, err := b.channel.Send("setOffline", map[string]interface{}{
   145  		"offline": offline,
   146  	})
   147  	return err
   148  }
   149  
   150  func (b *browserContextImpl) AddInitScript(options BrowserContextAddInitScriptOptions) error {
   151  	var source string
   152  	if options.Script != nil {
   153  		source = *options.Script
   154  	}
   155  	if options.Path != nil {
   156  		content, err := ioutil.ReadFile(*options.Path)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		source = string(content)
   161  	}
   162  	_, err := b.channel.Send("addInitScript", map[string]interface{}{
   163  		"source": source,
   164  	})
   165  	return err
   166  }
   167  
   168  func (b *browserContextImpl) ExposeBinding(name string, binding BindingCallFunction, handle ...bool) error {
   169  	needsHandle := false
   170  	if len(handle) == 1 {
   171  		needsHandle = handle[0]
   172  	}
   173  	for _, page := range b.pages {
   174  		if _, ok := page.(*pageImpl).bindings[name]; ok {
   175  			return fmt.Errorf("Function '%s' has been already registered in one of the pages", name)
   176  		}
   177  	}
   178  	if _, ok := b.bindings[name]; ok {
   179  		return fmt.Errorf("Function '%s' has been already registered", name)
   180  	}
   181  	b.bindings[name] = binding
   182  	_, err := b.channel.Send("exposeBinding", map[string]interface{}{
   183  		"name":        name,
   184  		"needsHandle": needsHandle,
   185  	})
   186  	return err
   187  }
   188  
   189  func (b *browserContextImpl) ExposeFunction(name string, binding ExposedFunction) error {
   190  	return b.ExposeBinding(name, func(source *BindingSource, args ...interface{}) interface{} {
   191  		return binding(args...)
   192  	})
   193  }
   194  
   195  func (b *browserContextImpl) Route(url interface{}, handler routeHandler) error {
   196  	b.routes = append(b.routes, newRouteHandlerEntry(newURLMatcher(url), handler))
   197  	if len(b.routes) == 1 {
   198  		_, err := b.channel.Send("setNetworkInterceptionEnabled", map[string]interface{}{
   199  			"enabled": true,
   200  		})
   201  		if err != nil {
   202  			return err
   203  		}
   204  	}
   205  	return nil
   206  }
   207  
   208  func (b *browserContextImpl) Unroute(url interface{}, handlers ...routeHandler) error {
   209  	b.Lock()
   210  	defer b.Unlock()
   211  
   212  	routes, err := unroute(b.channel, b.routes, url, handlers...)
   213  	if err != nil {
   214  		return err
   215  	}
   216  	b.routes = routes
   217  
   218  	return nil
   219  }
   220  
   221  func (b *browserContextImpl) WaitForEvent(event string, predicate ...interface{}) interface{} {
   222  	return <-waitForEvent(b, event, predicate...)
   223  }
   224  
   225  func (b *browserContextImpl) ExpectEvent(event string, cb func() error) (interface{}, error) {
   226  	return newExpectWrapper(b.WaitForEvent, []interface{}{event}, cb)
   227  }
   228  
   229  func (b *browserContextImpl) Close() error {
   230  	if b.isClosedOrClosing {
   231  		return nil
   232  	}
   233  	b.Lock()
   234  	b.isClosedOrClosing = true
   235  	b.Unlock()
   236  	_, err := b.channel.Send("close")
   237  	return err
   238  }
   239  
   240  type StorageState struct {
   241  	Cookies []Cookie       `json:"cookies"`
   242  	Origins []OriginsState `json:"origins"`
   243  }
   244  
   245  type Cookie struct {
   246  	Name     string  `json:"name"`
   247  	Value    string  `json:"value"`
   248  	URL      string  `json:"url"`
   249  	Domain   string  `json:"domain"`
   250  	Path     string  `json:"path"`
   251  	Expires  float64 `json:"expires"`
   252  	HttpOnly bool    `json:"httpOnly"`
   253  	Secure   bool    `json:"secure"`
   254  	SameSite string  `json:"sameSite"`
   255  }
   256  type OriginsState struct {
   257  	Origin       string              `json:"origin"`
   258  	LocalStorage []LocalStorageEntry `json:"localStorage"`
   259  }
   260  
   261  type LocalStorageEntry struct {
   262  	Name  string `json:"name"`
   263  	Value string `json:"value"`
   264  }
   265  
   266  func (b *browserContextImpl) StorageState(paths ...string) (*StorageState, error) {
   267  	result, err := b.channel.SendReturnAsDict("storageState")
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  	if len(paths) == 1 {
   272  		file, err := os.Create(paths[0])
   273  		if err != nil {
   274  			return nil, err
   275  		}
   276  		if err := json.NewEncoder(file).Encode(result); err != nil {
   277  			return nil, err
   278  		}
   279  		if err := file.Close(); err != nil {
   280  			return nil, err
   281  		}
   282  	}
   283  	var storageState StorageState
   284  	remapMapToStruct(result, &storageState)
   285  	return &storageState, nil
   286  }
   287  
   288  func (b *browserContextImpl) onBinding(binding *bindingCallImpl) {
   289  	function := b.bindings[binding.initializer["name"].(string)]
   290  	if function == nil {
   291  		return
   292  	}
   293  	go binding.Call(function)
   294  }
   295  
   296  func (b *browserContextImpl) onClose() {
   297  	b.isClosedOrClosing = true
   298  	if b.browser != nil {
   299  		contexts := make([]BrowserContext, 0)
   300  		b.browser.Lock()
   301  		for _, context := range b.browser.contexts {
   302  			if context != b {
   303  				contexts = append(contexts, context)
   304  			}
   305  		}
   306  		b.browser.contexts = contexts
   307  		b.browser.Unlock()
   308  	}
   309  	b.Emit("close")
   310  }
   311  
   312  func (b *browserContextImpl) onPage(page *pageImpl) {
   313  	page.setBrowserContext(b)
   314  	b.Lock()
   315  	b.pages = append(b.pages, page)
   316  	b.Unlock()
   317  	b.Emit("page", page)
   318  	opener, _ := page.Opener()
   319  	if opener != nil && !opener.IsClosed() {
   320  		opener.Emit("popup", page)
   321  	}
   322  }
   323  
   324  func (b *browserContextImpl) onRoute(route *routeImpl, request *requestImpl) {
   325  	go func() {
   326  		for _, handlerEntry := range b.routes {
   327  			if handlerEntry.matcher.Matches(request.URL()) {
   328  				handlerEntry.handler(route, request)
   329  				return
   330  			}
   331  		}
   332  		if err := route.Continue(); err != nil {
   333  			log.Printf("could not continue request: %v", err)
   334  		}
   335  	}()
   336  }
   337  func (p *browserContextImpl) Pause() error {
   338  	_, err := p.channel.Send("pause")
   339  	return err
   340  }
   341  
   342  func (b *browserContextImpl) OnBackgroundPage(ev map[string]interface{}) {
   343  	b.Lock()
   344  	p := fromChannel(ev["page"]).(*pageImpl)
   345  	p.browserContext = b
   346  	b.backgroundPages = append(b.backgroundPages, p)
   347  	b.Unlock()
   348  	b.Emit("backgroundpage", p)
   349  }
   350  
   351  func (b *browserContextImpl) BackgroundPages() []Page {
   352  	b.Lock()
   353  	defer b.Unlock()
   354  	return b.backgroundPages
   355  }
   356  
   357  func newBrowserContext(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *browserContextImpl {
   358  	bt := &browserContextImpl{
   359  		timeoutSettings: newTimeoutSettings(nil),
   360  		pages:           make([]Page, 0),
   361  		routes:          make([]*routeHandlerEntry, 0),
   362  		bindings:        make(map[string]BindingCallFunction),
   363  	}
   364  	bt.createChannelOwner(bt, parent, objectType, guid, initializer)
   365  	bt.tracing = fromChannel(initializer["tracing"]).(*tracingImpl)
   366  	bt.channel.On("bindingCall", func(params map[string]interface{}) {
   367  		bt.onBinding(fromChannel(params["binding"]).(*bindingCallImpl))
   368  	})
   369  	bt.channel.On("request", func(ev map[string]interface{}) {
   370  		request := fromChannel(ev["request"]).(*requestImpl)
   371  		page := fromNullableChannel(ev["page"])
   372  		bt.Emit("request", request)
   373  		if page != nil {
   374  			page.(*pageImpl).Emit("request", request)
   375  		}
   376  	})
   377  	bt.channel.On("requestFailed", func(ev map[string]interface{}) {
   378  		request := fromChannel(ev["request"]).(*requestImpl)
   379  		failureText := ev["failureText"]
   380  		if failureText != nil {
   381  			request.failureText = failureText.(string)
   382  		}
   383  		page := fromNullableChannel(ev["page"])
   384  		if request.timing != nil {
   385  			request.timing.ResponseEnd = ev["responseEndTiming"].(float64)
   386  		}
   387  		bt.Emit("requestfailed", request)
   388  		if page != nil {
   389  			page.(*pageImpl).Emit("requestfailed", request)
   390  		}
   391  	})
   392  
   393  	bt.channel.On("requestFinished", func(ev map[string]interface{}) {
   394  		request := fromChannel(ev["request"]).(*requestImpl)
   395  		response := fromNullableChannel(ev["response"])
   396  		if response != nil {
   397  			response.(*responseImpl).finished <- true
   398  		}
   399  		page := fromNullableChannel(ev["page"])
   400  		if request.timing != nil {
   401  			request.timing.ResponseEnd = ev["responseEndTiming"].(float64)
   402  		}
   403  		bt.Emit("requestfinished", request)
   404  		if page != nil {
   405  			page.(*pageImpl).Emit("requestfinished", request)
   406  		}
   407  	})
   408  	bt.channel.On("response", func(ev map[string]interface{}) {
   409  		response := fromChannel(ev["response"]).(*responseImpl)
   410  		page := fromNullableChannel(ev["page"])
   411  		bt.Emit("response", response)
   412  		if page != nil {
   413  			page.(*pageImpl).Emit("response", response)
   414  		}
   415  	})
   416  	bt.channel.On("close", bt.onClose)
   417  	bt.channel.On("page", func(payload map[string]interface{}) {
   418  		bt.onPage(fromChannel(payload["page"]).(*pageImpl))
   419  	})
   420  	bt.channel.On("route", func(params map[string]interface{}) {
   421  		bt.onRoute(fromChannel(params["route"]).(*routeImpl), fromChannel(params["request"]).(*requestImpl))
   422  	})
   423  	bt.channel.On("backgroundPage", bt.OnBackgroundPage)
   424  	return bt
   425  }
   426  

View as plain text