How to use Detach method of PuppeteerSharp.DOMWorld class

Best Puppeteer-sharp code snippet using PuppeteerSharp.DOMWorld.Detach

Run Puppeteer-sharp automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

FrameManager.cs

Source: FrameManager.cs Github

copy
1using PuppeteerSharp.Helpers;
2using PuppeteerSharp.Helpers.Json;
3using PuppeteerSharp.Messaging;
4using System;
5using System.Collections.Concurrent;
6using System.Collections.Generic;
7using System.Diagnostics.Contracts;
8using System.Linq;
9using System.Threading.Tasks;
10
11namespace PuppeteerSharp
12{
13    internal class FrameManager
14    {
15        private Dictionary<int, ExecutionContext> _contextIdToContext;
16        private bool _ensureNewDocumentNavigation;
17        private readonly ConcurrentDictionary<string, Frame> _frames;
18        private const string RefererHeaderName = "referer";
19        private readonly AsyncDictionaryHelper<string, Frame> _asyncFrames;
20        private readonly List<string> _isolatedWorlds = new List<string>();
21        private const string UtilityWorldName = "__puppeteer_utility_world__";
22
23        private FrameManager(CDPSession client, Page page, bool ignoreHTTPSErrors, TimeoutSettings timeoutSettings)
24        {
25            Client = client;
26            Page = page;
27            _frames = new ConcurrentDictionary<string, Frame>();
28            _contextIdToContext = new Dictionary<int, ExecutionContext>();
29            NetworkManager = new NetworkManager(client, ignoreHTTPSErrors, this);
30            TimeoutSettings = timeoutSettings;
31            _asyncFrames = new AsyncDictionaryHelper<string, Frame>(_frames, "Frame {0} not found");
32
33            Client.MessageReceived += Client_MessageReceived;
34        }
35
36        #region Properties
37
38        internal event EventHandler<FrameEventArgs> FrameAttached;
39        internal event EventHandler<FrameEventArgs> FrameDetached;
40        internal event EventHandler<FrameEventArgs> FrameNavigated;
41        internal event EventHandler<FrameEventArgs> FrameNavigatedWithinDocument;
42        internal event EventHandler<FrameEventArgs> LifecycleEvent;
43
44        internal CDPSession Client { get; }
45        internal NetworkManager NetworkManager { get; }
46        internal Frame MainFrame { get; set; }
47        internal Page Page { get; }
48        internal TimeoutSettings TimeoutSettings { get; }
49        #endregion
50
51        #region Public Methods
52        internal static async Task<FrameManager> CreateFrameManagerAsync(
53            CDPSession client,
54            Page page,
55            bool ignoreHTTPSErrors,
56            TimeoutSettings timeoutSettings)
57        {
58            var frameManager = new FrameManager(client, page, ignoreHTTPSErrors, timeoutSettings);
59            var getFrameTreeTask = client.SendAsync<PageGetFrameTreeResponse>("Page.getFrameTree");
60
61            await Task.WhenAll(
62                client.SendAsync("Page.enable"),
63                getFrameTreeTask).ConfigureAwait(false);
64
65            await frameManager.HandleFrameTreeAsync(new FrameTree(getFrameTreeTask.Result.FrameTree)).ConfigureAwait(false);
66
67            await Task.WhenAll(
68                client.SendAsync("Page.setLifecycleEventsEnabled", new PageSetLifecycleEventsEnabledRequest { Enabled = true }),
69                client.SendAsync("Runtime.enable"),
70                frameManager.NetworkManager.InitializeAsync()).ConfigureAwait(false);
71
72            await frameManager.EnsureIsolatedWorldAsync().ConfigureAwait(false);
73
74            return frameManager;
75        }
76
77        internal ExecutionContext ExecutionContextById(int contextId)
78        {
79            _contextIdToContext.TryGetValue(contextId, out var context);
80            return context;
81        }
82
83        public async Task<Response> NavigateFrameAsync(Frame frame, string url, NavigationOptions options)
84        {
85            var referrer = string.IsNullOrEmpty(options.Referer)
86               ? NetworkManager.ExtraHTTPHeaders?.GetValueOrDefault(RefererHeaderName)
87               : options.Referer;
88            var requests = new Dictionary<string, Request>();
89            var timeout = options?.Timeout ?? TimeoutSettings.NavigationTimeout;
90
91            using (var watcher = new LifecycleWatcher(this, frame, options?.WaitUntil, timeout))
92            {
93                try
94                {
95                    var navigateTask = NavigateAsync(Client, url, referrer, frame.Id);
96                    var task = await Task.WhenAny(
97                        watcher.TimeoutOrTerminationTask,
98                        navigateTask).ConfigureAwait(false);
99
100                    await task.ConfigureAwait(false);
101
102                    task = await Task.WhenAny(
103                        watcher.TimeoutOrTerminationTask,
104                        _ensureNewDocumentNavigation ? watcher.NewDocumentNavigationTask : watcher.SameDocumentNavigationTask)
105                        .ConfigureAwait(false);
106
107                    await task.ConfigureAwait(false);
108                }
109                catch (Exception ex)
110                {
111                    throw new NavigationException(ex.Message, ex);
112                }
113
114                return watcher.NavigationResponse;
115            }
116        }
117
118        private async Task NavigateAsync(CDPSession client, string url, string referrer, string frameId)
119        {
120            var response = await client.SendAsync<PageNavigateResponse>("Page.navigate", new PageNavigateRequest
121            {
122                Url = url,
123                Referrer = referrer ?? string.Empty,
124                FrameId = frameId
125            }).ConfigureAwait(false);
126
127            _ensureNewDocumentNavigation = !string.IsNullOrEmpty(response.LoaderId);
128
129            if (!string.IsNullOrEmpty(response.ErrorText))
130            {
131                throw new NavigationException(response.ErrorText, url);
132            }
133        }
134
135        public async Task<Response> WaitForFrameNavigationAsync(Frame frame, NavigationOptions options = null)
136        {
137            var timeout = options?.Timeout ?? TimeoutSettings.NavigationTimeout;
138            using (var watcher = new LifecycleWatcher(this, frame, options?.WaitUntil, timeout))
139            {
140                var raceTask = await Task.WhenAny(
141                    watcher.NewDocumentNavigationTask,
142                    watcher.SameDocumentNavigationTask,
143                    watcher.TimeoutOrTerminationTask
144                ).ConfigureAwait(false);
145
146                await raceTask.ConfigureAwait(false);
147
148                return watcher.NavigationResponse;
149            }
150        }
151
152        #endregion
153
154        #region Private Methods
155
156        private async void Client_MessageReceived(object sender, MessageEventArgs e)
157        {
158            try
159            {
160                switch (e.MessageID)
161                {
162                    case "Page.frameAttached":
163                        OnFrameAttached(e.MessageData.ToObject<PageFrameAttachedResponse>());
164                        break;
165
166                    case "Page.frameNavigated":
167                        await OnFrameNavigatedAsync(e.MessageData.ToObject<PageFrameNavigatedResponse>(true).Frame).ConfigureAwait(false);
168                        break;
169
170                    case "Page.navigatedWithinDocument":
171                        OnFrameNavigatedWithinDocument(e.MessageData.ToObject<NavigatedWithinDocumentResponse>(true));
172                        break;
173
174                    case "Page.frameDetached":
175                        OnFrameDetached(e.MessageData.ToObject<BasicFrameResponse>(true));
176                        break;
177
178                    case "Page.frameStoppedLoading":
179                        OnFrameStoppedLoading(e.MessageData.ToObject<BasicFrameResponse>(true));
180                        break;
181
182                    case "Runtime.executionContextCreated":
183                        await OnExecutionContextCreatedAsync(e.MessageData.ToObject<RuntimeExecutionContextCreatedResponse>(true).Context).ConfigureAwait(false);
184                        break;
185
186                    case "Runtime.executionContextDestroyed":
187                        OnExecutionContextDestroyed(e.MessageData.ToObject<RuntimeExecutionContextDestroyedResponse>(true).ExecutionContextId);
188                        break;
189                    case "Runtime.executionContextsCleared":
190                        OnExecutionContextsCleared();
191                        break;
192                    case "Page.lifecycleEvent":
193                        OnLifeCycleEvent(e.MessageData.ToObject<LifecycleEventResponse>(true));
194                        break;
195                    default:
196                        break;
197                }
198            }
199            catch (Exception ex)
200            {
201                var message = $"Connection failed to process {e.MessageID}. {ex.Message}. {ex.StackTrace}";
202                Client.Close(message);
203            }
204        }
205
206        private void OnFrameStoppedLoading(BasicFrameResponse e)
207        {
208            if (_frames.TryGetValue(e.FrameId, out var frame))
209            {
210                frame.OnLoadingStopped();
211                LifecycleEvent?.Invoke(this, new FrameEventArgs(frame));
212            }
213        }
214
215        private void OnLifeCycleEvent(LifecycleEventResponse e)
216        {
217            if (_frames.TryGetValue(e.FrameId, out var frame))
218            {
219                frame.OnLifecycleEvent(e.LoaderId, e.Name);
220                LifecycleEvent?.Invoke(this, new FrameEventArgs(frame));
221            }
222        }
223
224        private void OnExecutionContextsCleared()
225        {
226            while (_contextIdToContext.Count > 0)
227            {
228                var contextItem = _contextIdToContext.ElementAt(0);
229                _contextIdToContext.Remove(contextItem.Key);
230
231                if (contextItem.Value.World != null)
232                {
233                    contextItem.Value.World.SetContext(null);
234                }
235            }
236        }
237
238        private void OnExecutionContextDestroyed(int executionContextId)
239        {
240            _contextIdToContext.TryGetValue(executionContextId, out var context);
241
242            if (context != null)
243            {
244                _contextIdToContext.Remove(executionContextId);
245
246                if (context.World != null)
247                {
248                    context.World.SetContext(null);
249                }
250            }
251        }
252
253        private async Task OnExecutionContextCreatedAsync(ContextPayload contextPayload)
254        {
255            var frameId = contextPayload.AuxData?.FrameId;
256            var frame = !string.IsNullOrEmpty(frameId) ? await GetFrameAsync(frameId).ConfigureAwait(false) : null;
257            DOMWorld world = null;
258
259            if (frame != null)
260            {
261                if (contextPayload.AuxData?.IsDefault == true)
262                {
263                    world = frame.MainWorld;
264                }
265                else if (contextPayload.Name == UtilityWorldName && !frame.SecondaryWorld.HasContext)
266                {
267                    // In case of multiple sessions to the same target, there's a race between
268                    // connections so we might end up creating multiple isolated worlds.
269                    // We can use either.
270                    world = frame.SecondaryWorld;
271                }
272            }
273            if (contextPayload.AuxData?.Type == DOMWorldType.Isolated)
274            {
275                _isolatedWorlds.Add(contextPayload.Name);
276            }
277            var context = new ExecutionContext(Client, contextPayload, world);
278            if (world != null)
279            {
280                world.SetContext(context);
281            }
282            _contextIdToContext[contextPayload.Id] = context;
283        }
284
285        private void OnFrameDetached(BasicFrameResponse e)
286        {
287            if (_frames.TryGetValue(e.FrameId, out var frame))
288            {
289                RemoveFramesRecursively(frame);
290            }
291        }
292
293        private async Task OnFrameNavigatedAsync(FramePayload framePayload)
294        {
295            var isMainFrame = string.IsNullOrEmpty(framePayload.ParentId);
296            var frame = isMainFrame ? MainFrame : await GetFrameAsync(framePayload.Id).ConfigureAwait(false);
297
298            Contract.Assert(isMainFrame || frame != null, "We either navigate top level or have old version of the navigated frame");
299
300            // Detach all child frames first.
301            if (frame != null)
302            {
303                while (frame.ChildFrames.Count > 0)
304                {
305                    RemoveFramesRecursively(frame.ChildFrames[0]);
306                }
307            }
308
309            // Update or create main frame.
310            if (isMainFrame)
311            {
312                if (frame != null)
313                {
314                    // Update frame id to retain frame identity on cross-process navigation.
315                    if (frame.Id != null)
316                    {
317                        _frames.TryRemove(frame.Id, out _);
318                    }
319                    frame.Id = framePayload.Id;
320                }
321                else
322                {
323                    // Initial main frame navigation.
324                    frame = new Frame(this, Client, null, framePayload.Id);
325                }
326                _asyncFrames.AddItem(framePayload.Id, frame);
327                MainFrame = frame;
328            }
329
330            // Update frame payload.
331            frame.Navigated(framePayload);
332
333            FrameNavigated?.Invoke(this, new FrameEventArgs(frame));
334        }
335
336        internal Frame[] GetFrames() => _frames.Values.ToArray();
337
338        private void OnFrameNavigatedWithinDocument(NavigatedWithinDocumentResponse e)
339        {
340            if (_frames.TryGetValue(e.FrameId, out var frame))
341            {
342                frame.NavigatedWithinDocument(e.Url);
343
344                var eventArgs = new FrameEventArgs(frame);
345                FrameNavigatedWithinDocument?.Invoke(this, eventArgs);
346                FrameNavigated?.Invoke(this, eventArgs);
347            }
348        }
349
350        private void RemoveFramesRecursively(Frame frame)
351        {
352            while (frame.ChildFrames.Count > 0)
353            {
354                RemoveFramesRecursively(frame.ChildFrames[0]);
355            }
356            frame.Detach();
357            _frames.TryRemove(frame.Id, out _);
358            FrameDetached?.Invoke(this, new FrameEventArgs(frame));
359        }
360
361        private void OnFrameAttached(PageFrameAttachedResponse frameAttached)
362            => OnFrameAttached(frameAttached.FrameId, frameAttached.ParentFrameId);
363
364        private void OnFrameAttached(string frameId, string parentFrameId)
365        {
366            if (!_frames.ContainsKey(frameId) && _frames.ContainsKey(parentFrameId))
367            {
368                var parentFrame = _frames[parentFrameId];
369                var frame = new Frame(this, Client, parentFrame, frameId);
370                _frames[frame.Id] = frame;
371                FrameAttached?.Invoke(this, new FrameEventArgs(frame));
372            }
373        }
374
375        private async Task HandleFrameTreeAsync(FrameTree frameTree)
376        {
377            if (!string.IsNullOrEmpty(frameTree.Frame.ParentId))
378            {
379                OnFrameAttached(frameTree.Frame.Id, frameTree.Frame.ParentId);
380            }
381
382            await OnFrameNavigatedAsync(frameTree.Frame).ConfigureAwait(false);
383
384            if (frameTree.Childs != null)
385            {
386                foreach (var child in frameTree.Childs)
387                {
388                    await HandleFrameTreeAsync(child).ConfigureAwait(false);
389                }
390            }
391        }
392
393        private Task EnsureIsolatedWorldAsync() => EnsureIsolatedWorldAsync(UtilityWorldName);
394
395        private async Task EnsureIsolatedWorldAsync(string name)
396        {
397            if (_isolatedWorlds.Contains(name))
398            {
399                return;
400            }
401            _isolatedWorlds.Add(name);
402            await Client.SendAsync("Page.addScriptToEvaluateOnNewDocument", new PageAddScriptToEvaluateOnNewDocumentRequest
403            {
404                Source = $"//# sourceURL={ExecutionContext.EvaluationScriptUrl}",
405                WorldName = name,
406            }).ConfigureAwait(false);
407
408            try
409            {
410                await Task.WhenAll(GetFrames().Select(frame => Client.SendAsync("Page.createIsolatedWorld", new PageCreateIsolatedWorldRequest
411                {
412                    FrameId = frame.Id,
413                    GrantUniveralAccess = true,
414                    WorldName = name
415                }))).ConfigureAwait(false);
416            }
417            catch (PuppeteerException)
418            {
419            }
420        }
421
422        internal Task<Frame> GetFrameAsync(string frameId) => _asyncFrames.GetItemAsync(frameId);
423
424        internal Task<Frame> TryGetFrameAsync(string frameId) => _asyncFrames.TryGetItemAsync(frameId);
425
426        #endregion
427    }
428}
429
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Trigger Detach code on LambdaTest Cloud Grid

Execute automation tests with Detach on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)