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
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