Web Inspector: DOM Debugger: descendant breakpoints should be able to be enabled...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Controllers / DOMDebuggerManager.js
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object
27 {
28     constructor()
29     {
30         super();
31
32         this._domBreakpointURLMap = new Multimap;
33         this._domBreakpointFrameIdentifierMap = new Map;
34
35         this._eventBreakpoints = [];
36
37         this._urlBreakpoints = [];
38         this._allRequestsBreakpointEnabledSetting = new WI.Setting("break-on-all-requests", false);
39
40         this._allRequestsBreakpoint = new WI.URLBreakpoint(WI.URLBreakpoint.Type.Text, "", {
41             disabled: !this._allRequestsBreakpointEnabledSetting.value,
42         });
43
44         WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DisabledStateChanged, this._handleDOMBreakpointDisabledStateChanged, this);
45         WI.EventBreakpoint.addEventListener(WI.EventBreakpoint.Event.DisabledStateChanged, this._handleEventBreakpointDisabledStateChanged, this);
46         WI.URLBreakpoint.addEventListener(WI.URLBreakpoint.Event.DisabledStateChanged, this._handleURLBreakpointDisabledStateChanged, this);
47
48         WI.domManager.addEventListener(WI.DOMManager.Event.NodeRemoved, this._nodeRemoved, this);
49         WI.domManager.addEventListener(WI.DOMManager.Event.NodeInserted, this._nodeInserted, this);
50
51         WI.networkManager.addEventListener(WI.NetworkManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
52
53         WI.Frame.addEventListener(WI.Frame.Event.ChildFrameWasRemoved, this._childFrameWasRemoved, this);
54         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
55
56         let loadBreakpoints = async (constructor, objectStore, oldSettings, callback) => {
57             for (let key of oldSettings) {
58                 let existingSerializedBreakpoints = WI.Setting.migrateValue(key);
59                 if (existingSerializedBreakpoints) {
60                     for (let existingSerializedBreakpoint of existingSerializedBreakpoints)
61                         await objectStore.putObject(constructor.deserialize(existingSerializedBreakpoint));
62                 }
63             }
64
65             let serializedBreakpoints = await objectStore.getAll();
66
67             this._restoringBreakpoints = true;
68             for (let serializedBreakpoint of serializedBreakpoints) {
69                 let breakpoint = constructor.deserialize(serializedBreakpoint);
70
71                 const key = null;
72                 objectStore.associateObject(breakpoint, key, serializedBreakpoint);
73
74                 callback(breakpoint);
75             }
76             this._restoringBreakpoints = false;
77         };
78
79         if (this.supported) {
80             loadBreakpoints(WI.DOMBreakpoint, WI.objectStores.domBreakpoints, ["dom-breakpoints"], (breakpoint) => {
81                 this.addDOMBreakpoint(breakpoint);
82             });
83
84             loadBreakpoints(WI.EventBreakpoint, WI.objectStores.eventBreakpoints, ["event-breakpoints"], (breakpoint) => {
85                 this.addEventBreakpoint(breakpoint);
86             });
87
88             loadBreakpoints(WI.URLBreakpoint, WI.objectStores.urlBreakpoints, ["xhr-breakpoints", "url-breakpoints"], (breakpoint) => {
89                 this.addURLBreakpoint(breakpoint);
90             });
91         }
92     }
93
94     // Target
95
96     initializeTarget(target)
97     {
98         if (target.DOMDebuggerAgent) {
99             if (target === WI.assumingMainTarget() && target.mainResource)
100                 this._speculativelyResolveDOMBreakpointsForURL(target.mainResource.url);
101
102             for (let breakpoint of this._eventBreakpoints) {
103                 if (!breakpoint.disabled)
104                     this._updateEventBreakpoint(breakpoint, target);
105             }
106
107             for (let breakpoint of this._urlBreakpoints) {
108                 if (!breakpoint.disabled)
109                     this._updateURLBreakpoint(breakpoint, target);
110             }
111
112             if (!this._allRequestsBreakpoint.disabled)
113                 this._updateURLBreakpoint(this._allRequestsBreakpoint, target);
114         }
115     }
116
117     // Static
118
119     static supportsEventBreakpoints()
120     {
121         return InspectorBackend.domains.DOMDebugger.setEventBreakpoint && InspectorBackend.domains.DOMDebugger.removeEventBreakpoint;
122     }
123
124     static supportsURLBreakpoints()
125     {
126         return InspectorBackend.domains.DOMDebugger.setURLBreakpoint && InspectorBackend.domains.DOMDebugger.removeURLBreakpoint;
127     }
128
129     // Public
130
131     get supported()
132     {
133         return !!InspectorBackend.domains.DOMDebugger;
134     }
135
136     get allRequestsBreakpoint() { return this._allRequestsBreakpoint; }
137
138     get domBreakpoints()
139     {
140         let mainFrame = WI.networkManager.mainFrame;
141         if (!mainFrame)
142             return [];
143
144         let resolvedBreakpoints = [];
145         let frames = [mainFrame];
146         while (frames.length) {
147             let frame = frames.shift();
148             let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame.id);
149             if (domBreakpointNodeIdentifierMap)
150                 resolvedBreakpoints = resolvedBreakpoints.concat(Array.from(domBreakpointNodeIdentifierMap.values()));
151
152             frames.push(...frame.childFrameCollection);
153         }
154
155         return resolvedBreakpoints;
156     }
157
158     get eventBreakpoints() { return this._eventBreakpoints; }
159
160     get urlBreakpoints() { return this._urlBreakpoints; }
161
162     isBreakpointSpecial(breakpoint)
163     {
164         return breakpoint === this._allRequestsBreakpoint;
165     }
166
167     domBreakpointsForNode(node)
168     {
169         console.assert(node instanceof WI.DOMNode);
170
171         if (!node || !node.frame)
172             return [];
173
174         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame.id);
175         if (!domBreakpointNodeIdentifierMap)
176             return [];
177
178         let breakpoints = domBreakpointNodeIdentifierMap.get(node.id);
179         return breakpoints ? Array.from(breakpoints) : [];
180     }
181
182     domBreakpointsInSubtree(node)
183     {
184         console.assert(node instanceof WI.DOMNode);
185
186         let breakpoints = [];
187
188         if (node.children) {
189             let children = Array.from(node.children);
190             while (children.length) {
191                 let child = children.pop();
192                 if (child.children)
193                     children = children.concat(child.children);
194                 breakpoints = breakpoints.concat(this.domBreakpointsForNode(child));
195             }
196         }
197
198         return breakpoints;
199     }
200
201     addDOMBreakpoint(breakpoint)
202     {
203         console.assert(breakpoint instanceof WI.DOMBreakpoint);
204         if (!breakpoint || !breakpoint.url)
205             return;
206
207         if (this.isBreakpointSpecial(breakpoint)) {
208             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, {breakpoint});
209             return;
210         }
211
212         this._domBreakpointURLMap.add(breakpoint.url, breakpoint);
213
214         if (breakpoint.domNodeIdentifier)
215             this._resolveDOMBreakpoint(breakpoint, breakpoint.domNodeIdentifier);
216
217         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, {breakpoint});
218
219         if (!this._restoringBreakpoints)
220             WI.objectStores.domBreakpoints.putObject(breakpoint);
221     }
222
223     removeDOMBreakpoint(breakpoint)
224     {
225         console.assert(breakpoint instanceof WI.DOMBreakpoint);
226         if (!breakpoint)
227             return;
228
229         if (this.isBreakpointSpecial(breakpoint)) {
230             breakpoint.disabled = true;
231             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, {breakpoint});
232             return;
233         }
234
235         this._detachDOMBreakpoint(breakpoint);
236
237         this._domBreakpointURLMap.delete(breakpoint.url);
238
239         if (!breakpoint.disabled) {
240             // We should get the target associated with the nodeIdentifier of this breakpoint.
241             let target = WI.assumingMainTarget();
242             target.DOMDebuggerAgent.removeDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
243         }
244
245         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, {breakpoint});
246
247         breakpoint.domNodeIdentifier = null;
248
249         if (!this._restoringBreakpoints)
250             WI.objectStores.domBreakpoints.deleteObject(breakpoint);
251     }
252
253     removeDOMBreakpointsForNode(node)
254     {
255         this.domBreakpointsForNode(node).forEach(this.removeDOMBreakpoint, this);
256     }
257
258     eventBreakpointForTypeAndEventName(type, eventName)
259     {
260         return this._eventBreakpoints.find((breakpoint) => breakpoint.type === type && breakpoint.eventName === eventName) || null;
261     }
262
263     addEventBreakpoint(breakpoint)
264     {
265         console.assert(breakpoint instanceof WI.EventBreakpoint);
266         if (!breakpoint)
267             return;
268
269         if (this.isBreakpointSpecial(breakpoint)) {
270             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointAdded, {breakpoint});
271             return;
272         }
273
274         if (this.eventBreakpointForTypeAndEventName(breakpoint.type, breakpoint.eventName))
275             return;
276
277         this._eventBreakpoints.push(breakpoint);
278
279         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointAdded, {breakpoint});
280
281         if (!breakpoint.disabled) {
282             for (let target of WI.targets) {
283                 if (target.DOMDebuggerAgent)
284                     this._updateEventBreakpoint(breakpoint, target);
285             }
286         }
287
288         if (!this._restoringBreakpoints)
289             WI.objectStores.eventBreakpoints.putObject(breakpoint);
290     }
291
292     removeEventBreakpoint(breakpoint)
293     {
294         console.assert(breakpoint instanceof WI.EventBreakpoint);
295         if (!breakpoint)
296             return;
297
298         if (this.isBreakpointSpecial(breakpoint)) {
299             breakpoint.disabled = true;
300             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, {breakpoint});
301             return;
302         }
303
304         if (!this._eventBreakpoints.includes(breakpoint))
305             return;
306
307         this._eventBreakpoints.remove(breakpoint);
308
309         if (!this._restoringBreakpoints)
310             WI.objectStores.eventBreakpoints.deleteObject(breakpoint);
311
312         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, {breakpoint});
313
314         if (breakpoint.disabled)
315             return;
316
317         for (let target of WI.targets) {
318             if (target.DOMDebuggerAgent) {
319                 // Compatibility (iOS 12): DOMDebuggerAgent.removeEventBreakpoint did not exist.
320                 if (!WI.DOMDebuggerManager.supportsEventBreakpoints()) {
321                     console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
322                     target.DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName);
323                     continue;
324                 }
325
326                 target.DOMDebuggerAgent.removeEventBreakpoint(breakpoint.type, breakpoint.eventName);
327             }
328         }
329     }
330
331     urlBreakpointForURL(url)
332     {
333         return this._urlBreakpoints.find((breakpoint) => breakpoint.url === url) || null;
334     }
335
336     addURLBreakpoint(breakpoint)
337     {
338         console.assert(breakpoint instanceof WI.URLBreakpoint);
339         if (!breakpoint)
340             return;
341
342         if (this.isBreakpointSpecial(breakpoint)) {
343             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointAdded, {breakpoint});
344             return;
345         }
346
347         console.assert(!this._urlBreakpoints.includes(breakpoint), "Already added URL breakpoint.", breakpoint);
348         if (this._urlBreakpoints.includes(breakpoint))
349             return;
350
351         if (this._urlBreakpoints.some((entry) => entry.type === breakpoint.type && entry.url === breakpoint.url))
352             return;
353
354         this._urlBreakpoints.push(breakpoint);
355
356         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointAdded, {breakpoint});
357
358         if (!breakpoint.disabled) {
359             for (let target of WI.targets) {
360                 if (target.DOMDebuggerAgent)
361                     this._updateURLBreakpoint(breakpoint, target);
362             }
363         }
364
365         if (!this._restoringBreakpoints)
366             WI.objectStores.urlBreakpoints.putObject(breakpoint);
367     }
368
369     removeURLBreakpoint(breakpoint)
370     {
371         console.assert(breakpoint instanceof WI.URLBreakpoint);
372         if (!breakpoint)
373             return;
374
375         if (this.isBreakpointSpecial(breakpoint)) {
376             breakpoint.disabled = true;
377             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, {breakpoint});
378             return;
379         }
380
381         if (!this._urlBreakpoints.includes(breakpoint))
382             return;
383
384         this._urlBreakpoints.remove(breakpoint, true);
385
386         if (!this._restoringBreakpoints)
387             WI.objectStores.urlBreakpoints.deleteObject(breakpoint);
388
389         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, {breakpoint});
390
391         if (breakpoint.disabled)
392             return;
393
394         for (let target of WI.targets) {
395             if (target.DOMDebuggerAgent) {
396                 // Compatibility (iOS 12.1): DOMDebuggerAgent.removeURLBreakpoint did not exist.
397                 if (WI.DOMDebuggerManager.supportsURLBreakpoints())
398                     target.DOMDebuggerAgent.removeURLBreakpoint(breakpoint.url);
399                 else
400                     target.DOMDebuggerAgent.removeXHRBreakpoint(breakpoint.url);
401             }
402         }
403     }
404
405     // Private
406
407     _detachDOMBreakpoint(breakpoint)
408     {
409         let nodeIdentifier = breakpoint.domNodeIdentifier;
410         let node = WI.domManager.nodeForId(nodeIdentifier);
411         console.assert(node, "Missing DOM node for breakpoint.", breakpoint);
412         if (!node || !node.frame)
413             return;
414
415         let frameIdentifier = node.frame.id;
416         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frameIdentifier);
417         console.assert(domBreakpointNodeIdentifierMap, "Missing DOM breakpoints for node parent frame.", node);
418         if (!domBreakpointNodeIdentifierMap)
419             return;
420
421         domBreakpointNodeIdentifierMap.delete(nodeIdentifier, breakpoint);
422
423         if (!domBreakpointNodeIdentifierMap.size)
424             this._domBreakpointFrameIdentifierMap.delete(frameIdentifier);
425     }
426
427     _detachBreakpointsForFrame(frame)
428     {
429         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame.id);
430         if (!domBreakpointNodeIdentifierMap)
431             return;
432
433         this._domBreakpointFrameIdentifierMap.delete(frame.id);
434
435         for (let breakpoint of domBreakpointNodeIdentifierMap.values())
436             breakpoint.domNodeIdentifier = null;
437     }
438
439     _speculativelyResolveDOMBreakpointsForURL(url)
440     {
441         let domBreakpoints = this._domBreakpointURLMap.get(url);
442         if (!domBreakpoints)
443             return;
444
445         for (let breakpoint of domBreakpoints) {
446             if (breakpoint.domNodeIdentifier)
447                 continue;
448
449             WI.domManager.pushNodeByPathToFrontend(breakpoint.path, (nodeIdentifier) => {
450                 if (breakpoint.domNodeIdentifier) {
451                     // This breakpoint may have been resolved by a node being inserted before this
452                     // callback is invoked.  If so, the `nodeIdentifier` should match, so don't try
453                     // to resolve it again as it would've already been resolved.
454                     console.assert(breakpoint.domNodeIdentifier === nodeIdentifier);
455                     return;
456                 }
457
458                 if (nodeIdentifier)
459                     this._resolveDOMBreakpoint(breakpoint, nodeIdentifier);
460             });
461         }
462     }
463
464     _resolveDOMBreakpoint(breakpoint, nodeIdentifier)
465     {
466         let node = WI.domManager.nodeForId(nodeIdentifier);
467         console.assert(node, "Missing DOM node for nodeIdentifier.", nodeIdentifier);
468         if (!node || !node.frame)
469             return;
470
471         let frameIdentifier = node.frame.id;
472         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frameIdentifier);
473         if (!domBreakpointNodeIdentifierMap) {
474             domBreakpointNodeIdentifierMap = new Multimap;
475             this._domBreakpointFrameIdentifierMap.set(frameIdentifier, domBreakpointNodeIdentifierMap);
476         }
477
478         domBreakpointNodeIdentifierMap.add(nodeIdentifier, breakpoint);
479
480         breakpoint.domNodeIdentifier = nodeIdentifier;
481
482         if (!breakpoint.disabled) {
483             // We should get the target associated with the nodeIdentifier of this breakpoint.
484             let target = WI.assumingMainTarget();
485             if (target && target.DOMDebuggerAgent)
486                 this._updateDOMBreakpoint(breakpoint, target);
487         }
488     }
489
490     _updateDOMBreakpoint(breakpoint, target)
491     {
492         console.assert(target.DOMDebuggerAgent);
493
494         if (!breakpoint.domNodeIdentifier)
495             return;
496
497         if (breakpoint.disabled)
498             target.DOMDebuggerAgent.removeDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
499         else {
500             if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
501                 WI.debuggerManager.breakpointsEnabled = true;
502
503             target.DOMDebuggerAgent.setDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
504         }
505     }
506
507     _updateEventBreakpoint(breakpoint, target)
508     {
509         console.assert(target.DOMDebuggerAgent);
510
511         // Compatibility (iOS 12): DOMDebuggerAgent.removeEventBreakpoint did not exist.
512         if (!WI.DOMDebuggerManager.supportsEventBreakpoints()) {
513             console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
514             if (breakpoint.disabled)
515                 target.DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName);
516             else {
517                 if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
518                     WI.debuggerManager.breakpointsEnabled = true;
519
520                 target.DOMDebuggerAgent.setEventListenerBreakpoint(breakpoint.eventName);
521             }
522             return;
523         }
524
525         if (breakpoint.disabled)
526             target.DOMDebuggerAgent.removeEventBreakpoint(breakpoint.type, breakpoint.eventName);
527         else {
528             if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
529                 WI.debuggerManager.breakpointsEnabled = true;
530
531             target.DOMDebuggerAgent.setEventBreakpoint(breakpoint.type, breakpoint.eventName);
532         }
533     }
534
535     _updateURLBreakpoint(breakpoint, target)
536     {
537         console.assert(target.DOMDebuggerAgent);
538
539         // Compatibility (iOS 12.1): DOMDebuggerAgent.removeURLBreakpoint did not exist.
540         if (!WI.DOMDebuggerManager.supportsURLBreakpoints()) {
541             if (breakpoint.disabled)
542                 target.DOMDebuggerAgent.removeXHRBreakpoint(breakpoint.url);
543             else {
544                 if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
545                     WI.debuggerManager.breakpointsEnabled = true;
546
547                 let isRegex = breakpoint.type === WI.URLBreakpoint.Type.RegularExpression;
548                 target.DOMDebuggerAgent.setXHRBreakpoint(breakpoint.url, isRegex);
549             }
550             return;
551         }
552
553         if (breakpoint.disabled)
554             target.DOMDebuggerAgent.removeURLBreakpoint(breakpoint.url);
555         else {
556             if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
557                 WI.debuggerManager.breakpointsEnabled = true;
558
559             let isRegex = breakpoint.type === WI.URLBreakpoint.Type.RegularExpression;
560             target.DOMDebuggerAgent.setURLBreakpoint(breakpoint.url, isRegex);
561         }
562     }
563
564     _handleDOMBreakpointDisabledStateChanged(event)
565     {
566         let breakpoint = event.target;
567         let target = WI.assumingMainTarget();
568         if (target && target.DOMDebuggerAgent)
569             this._updateDOMBreakpoint(breakpoint, target);
570
571         if (!this._restoringBreakpoints)
572             WI.objectStores.domBreakpoints.putObject(breakpoint);
573     }
574
575     _handleEventBreakpointDisabledStateChanged(event)
576     {
577         let breakpoint = event.target;
578
579         // Specific event listener breakpoints are handled by `DOMManager`.
580         if (breakpoint.eventListener)
581             return;
582
583         for (let target of WI.targets) {
584             if (target.DOMDebuggerAgent)
585                 this._updateEventBreakpoint(breakpoint, target);
586         }
587
588         if (!this._restoringBreakpoints)
589             WI.objectStores.eventBreakpoints.putObject(breakpoint);
590     }
591
592     _handleURLBreakpointDisabledStateChanged(event)
593     {
594         let breakpoint = event.target;
595
596         if (breakpoint === this._allRequestsBreakpoint)
597             this._allRequestsBreakpointEnabledSetting.value = !breakpoint.disabled;
598
599         for (let target of WI.targets) {
600             if (target.DOMDebuggerAgent)
601                 this._updateURLBreakpoint(breakpoint, target);
602         }
603
604         if (!this._restoringBreakpoints)
605             WI.objectStores.urlBreakpoints.putObject(breakpoint);
606     }
607
608     _childFrameWasRemoved(event)
609     {
610         let frame = event.data.childFrame;
611         this._detachBreakpointsForFrame(frame);
612     }
613
614     _mainFrameDidChange(event)
615     {
616         this._speculativelyResolveDOMBreakpointsForURL(WI.networkManager.mainFrame.url);
617     }
618
619     _mainResourceDidChange(event)
620     {
621         let frame = event.target;
622         if (frame.isMainFrame()) {
623             for (let breakpoint of this._domBreakpointURLMap.values())
624                 breakpoint.domNodeIdentifier = null;
625
626             this._domBreakpointFrameIdentifierMap.clear();
627         } else
628             this._detachBreakpointsForFrame(frame);
629
630         this._speculativelyResolveDOMBreakpointsForURL(frame.url);
631     }
632
633     _nodeInserted(event)
634     {
635         let node = event.data.node;
636         if (node.nodeType() !== Node.ELEMENT_NODE || !node.frame)
637             return;
638
639         let url = node.frame.url;
640         let breakpoints = this._domBreakpointURLMap.get(url);
641         if (!breakpoints)
642             return;
643
644         for (let breakpoint of breakpoints) {
645             if (breakpoint.domNodeIdentifier)
646                 continue;
647
648             if (breakpoint.path !== node.path())
649                 continue;
650
651             this._resolveDOMBreakpoint(breakpoint, node.id);
652         }
653     }
654
655     _nodeRemoved(event)
656     {
657         let node = event.data.node;
658         if (node.nodeType() !== Node.ELEMENT_NODE || !node.frame)
659             return;
660
661         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame.id);
662         if (!domBreakpointNodeIdentifierMap)
663             return;
664
665         let breakpoints = domBreakpointNodeIdentifierMap.get(node.id);
666         if (!breakpoints)
667             return;
668
669         domBreakpointNodeIdentifierMap.delete(node.id);
670
671         if (!domBreakpointNodeIdentifierMap.size)
672             this._domBreakpointFrameIdentifierMap.delete(node.frame.id);
673
674         for (let breakpoint of breakpoints)
675             breakpoint.domNodeIdentifier = null;
676     }
677 };
678
679 WI.DOMDebuggerManager.Event = {
680     DOMBreakpointAdded: "dom-debugger-manager-dom-breakpoint-added",
681     DOMBreakpointRemoved: "dom-debugger-manager-dom-breakpoint-removed",
682     EventBreakpointAdded: "dom-debugger-manager-event-breakpoint-added",
683     EventBreakpointRemoved: "dom-debugger-manager-event-breakpoint-removed",
684     URLBreakpointAdded: "dom-debugger-manager-url-breakpoint-added",
685     URLBreakpointRemoved: "dom-debugger-manager-url-breakpoint-removed",
686 };