69ce6d199e23a9554019edab4dbbea6f9792d319
[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                 for (let breakpoints of domBreakpointNodeIdentifierMap.values())
151                     resolvedBreakpoints = resolvedBreakpoints.concat(breakpoints);
152             }
153
154             frames.push(...frame.childFrameCollection);
155         }
156
157         return resolvedBreakpoints;
158     }
159
160     get eventBreakpoints() { return this._eventBreakpoints; }
161
162     get urlBreakpoints() { return this._urlBreakpoints; }
163
164     isBreakpointSpecial(breakpoint)
165     {
166         return breakpoint === this._allRequestsBreakpoint;
167     }
168
169     domBreakpointsForNode(node)
170     {
171         console.assert(node instanceof WI.DOMNode);
172
173         if (!node || !node.frame)
174             return [];
175
176         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame.id);
177         if (!domBreakpointNodeIdentifierMap)
178             return [];
179
180         let breakpoints = domBreakpointNodeIdentifierMap.get(node.id);
181         return breakpoints ? breakpoints.slice() : [];
182     }
183
184     addDOMBreakpoint(breakpoint)
185     {
186         console.assert(breakpoint instanceof WI.DOMBreakpoint);
187         if (!breakpoint || !breakpoint.url)
188             return;
189
190         if (this.isBreakpointSpecial(breakpoint)) {
191             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, {breakpoint});
192             return;
193         }
194
195         this._domBreakpointURLMap.add(breakpoint.url, breakpoint);
196
197         if (breakpoint.domNodeIdentifier)
198             this._resolveDOMBreakpoint(breakpoint, breakpoint.domNodeIdentifier);
199
200         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, {breakpoint});
201
202         if (!this._restoringBreakpoints)
203             WI.objectStores.domBreakpoints.putObject(breakpoint);
204     }
205
206     removeDOMBreakpoint(breakpoint)
207     {
208         console.assert(breakpoint instanceof WI.DOMBreakpoint);
209         if (!breakpoint)
210             return;
211
212         if (this.isBreakpointSpecial(breakpoint)) {
213             breakpoint.disabled = true;
214             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, {breakpoint});
215             return;
216         }
217
218         let nodeIdentifier = breakpoint.domNodeIdentifier;
219         console.assert(nodeIdentifier, "Cannot remove unresolved DOM breakpoint.");
220         if (!nodeIdentifier)
221             return;
222
223         this._detachDOMBreakpoint(breakpoint);
224
225         this._domBreakpointURLMap.delete(breakpoint.url);
226
227         if (!breakpoint.disabled) {
228             // We should get the target associated with the nodeIdentifier of this breakpoint.
229             let target = WI.assumingMainTarget();
230             target.DOMDebuggerAgent.removeDOMBreakpoint(nodeIdentifier, breakpoint.type);
231         }
232
233         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, {breakpoint});
234
235         breakpoint.domNodeIdentifier = null;
236
237         if (!this._restoringBreakpoints)
238             WI.objectStores.domBreakpoints.deleteObject(breakpoint);
239     }
240
241     removeDOMBreakpointsForNode(node)
242     {
243         this.domBreakpointsForNode(node).forEach(this.removeDOMBreakpoint, this);
244     }
245
246     eventBreakpointForTypeAndEventName(type, eventName)
247     {
248         return this._eventBreakpoints.find((breakpoint) => breakpoint.type === type && breakpoint.eventName === eventName) || null;
249     }
250
251     addEventBreakpoint(breakpoint)
252     {
253         console.assert(breakpoint instanceof WI.EventBreakpoint);
254         if (!breakpoint)
255             return;
256
257         if (this.isBreakpointSpecial(breakpoint)) {
258             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointAdded, {breakpoint});
259             return;
260         }
261
262         if (this.eventBreakpointForTypeAndEventName(breakpoint.type, breakpoint.eventName))
263             return;
264
265         this._eventBreakpoints.push(breakpoint);
266
267         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointAdded, {breakpoint});
268
269         if (!breakpoint.disabled) {
270             for (let target of WI.targets) {
271                 if (target.DOMDebuggerAgent)
272                     this._updateEventBreakpoint(breakpoint, target);
273             }
274         }
275
276         if (!this._restoringBreakpoints)
277             WI.objectStores.eventBreakpoints.putObject(breakpoint);
278     }
279
280     removeEventBreakpoint(breakpoint)
281     {
282         console.assert(breakpoint instanceof WI.EventBreakpoint);
283         if (!breakpoint)
284             return;
285
286         if (this.isBreakpointSpecial(breakpoint)) {
287             breakpoint.disabled = true;
288             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, {breakpoint});
289             return;
290         }
291
292         if (!this._eventBreakpoints.includes(breakpoint))
293             return;
294
295         this._eventBreakpoints.remove(breakpoint);
296
297         if (!this._restoringBreakpoints)
298             WI.objectStores.eventBreakpoints.deleteObject(breakpoint);
299
300         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, {breakpoint});
301
302         if (breakpoint.disabled)
303             return;
304
305         for (let target of WI.targets) {
306             if (target.DOMDebuggerAgent) {
307                 // Compatibility (iOS 12): DOMDebuggerAgent.removeEventBreakpoint did not exist.
308                 if (!WI.DOMDebuggerManager.supportsEventBreakpoints()) {
309                     console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
310                     target.DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName);
311                     continue;
312                 }
313
314                 target.DOMDebuggerAgent.removeEventBreakpoint(breakpoint.type, breakpoint.eventName);
315             }
316         }
317     }
318
319     urlBreakpointForURL(url)
320     {
321         return this._urlBreakpoints.find((breakpoint) => breakpoint.url === url) || null;
322     }
323
324     addURLBreakpoint(breakpoint)
325     {
326         console.assert(breakpoint instanceof WI.URLBreakpoint);
327         if (!breakpoint)
328             return;
329
330         if (this.isBreakpointSpecial(breakpoint)) {
331             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointAdded, {breakpoint});
332             return;
333         }
334
335         console.assert(!this._urlBreakpoints.includes(breakpoint), "Already added URL breakpoint.", breakpoint);
336         if (this._urlBreakpoints.includes(breakpoint))
337             return;
338
339         if (this._urlBreakpoints.some((entry) => entry.type === breakpoint.type && entry.url === breakpoint.url))
340             return;
341
342         this._urlBreakpoints.push(breakpoint);
343
344         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointAdded, {breakpoint});
345
346         if (!breakpoint.disabled) {
347             for (let target of WI.targets) {
348                 if (target.DOMDebuggerAgent)
349                     this._updateURLBreakpoint(breakpoint, target);
350             }
351         }
352
353         if (!this._restoringBreakpoints)
354             WI.objectStores.urlBreakpoints.putObject(breakpoint);
355     }
356
357     removeURLBreakpoint(breakpoint)
358     {
359         console.assert(breakpoint instanceof WI.URLBreakpoint);
360         if (!breakpoint)
361             return;
362
363         if (this.isBreakpointSpecial(breakpoint)) {
364             breakpoint.disabled = true;
365             this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, {breakpoint});
366             return;
367         }
368
369         if (!this._urlBreakpoints.includes(breakpoint))
370             return;
371
372         this._urlBreakpoints.remove(breakpoint, true);
373
374         if (!this._restoringBreakpoints)
375             WI.objectStores.urlBreakpoints.deleteObject(breakpoint);
376
377         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, {breakpoint});
378
379         if (breakpoint.disabled)
380             return;
381
382         for (let target of WI.targets) {
383             if (target.DOMDebuggerAgent) {
384                 // Compatibility (iOS 12.1): DOMDebuggerAgent.removeURLBreakpoint did not exist.
385                 if (WI.DOMDebuggerManager.supportsURLBreakpoints())
386                     target.DOMDebuggerAgent.removeURLBreakpoint(breakpoint.url);
387                 else
388                     target.DOMDebuggerAgent.removeXHRBreakpoint(breakpoint.url);
389             }
390         }
391     }
392
393     // Private
394
395     _detachDOMBreakpoint(breakpoint)
396     {
397         let nodeIdentifier = breakpoint.domNodeIdentifier;
398         let node = WI.domManager.nodeForId(nodeIdentifier);
399         console.assert(node, "Missing DOM node for breakpoint.", breakpoint);
400         if (!node || !node.frame)
401             return;
402
403         let frameIdentifier = node.frame.id;
404         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frameIdentifier);
405         console.assert(domBreakpointNodeIdentifierMap, "Missing DOM breakpoints for node parent frame.", node);
406         if (!domBreakpointNodeIdentifierMap)
407             return;
408
409         let breakpoints = domBreakpointNodeIdentifierMap.get(nodeIdentifier);
410         console.assert(breakpoints, "Missing DOM breakpoints for node.", node);
411         if (!breakpoints)
412             return;
413
414         breakpoints.remove(breakpoint, true);
415
416         if (breakpoints.length)
417             return;
418
419         domBreakpointNodeIdentifierMap.delete(nodeIdentifier);
420
421         if (!domBreakpointNodeIdentifierMap.size)
422             this._domBreakpointFrameIdentifierMap.delete(frameIdentifier);
423     }
424
425     _detachBreakpointsForFrame(frame)
426     {
427         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame.id);
428         if (!domBreakpointNodeIdentifierMap)
429             return;
430
431         this._domBreakpointFrameIdentifierMap.delete(frame.id);
432
433         for (let breakpoints of domBreakpointNodeIdentifierMap.values()) {
434             for (let breakpoint of breakpoints)
435                 breakpoint.domNodeIdentifier = null;
436         }
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 (nodeIdentifier)
451                     this._resolveDOMBreakpoint(breakpoint, nodeIdentifier);
452             });
453         }
454     }
455
456     _resolveDOMBreakpoint(breakpoint, nodeIdentifier)
457     {
458         let node = WI.domManager.nodeForId(nodeIdentifier);
459         console.assert(node, "Missing DOM node for nodeIdentifier.", nodeIdentifier);
460         if (!node || !node.frame)
461             return;
462
463         let frameIdentifier = node.frame.id;
464         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frameIdentifier);
465         if (!domBreakpointNodeIdentifierMap) {
466             domBreakpointNodeIdentifierMap = new Map;
467             this._domBreakpointFrameIdentifierMap.set(frameIdentifier, domBreakpointNodeIdentifierMap);
468         }
469
470         let breakpoints = domBreakpointNodeIdentifierMap.get(nodeIdentifier);
471         if (breakpoints)
472             breakpoints.push(breakpoint);
473         else
474             domBreakpointNodeIdentifierMap.set(nodeIdentifier, [breakpoint]);
475
476         breakpoint.domNodeIdentifier = nodeIdentifier;
477
478         if (!breakpoint.disabled) {
479             // We should get the target associated with the nodeIdentifier of this breakpoint.
480             let target = WI.assumingMainTarget();
481             if (target && target.DOMDebuggerAgent)
482                 this._updateDOMBreakpoint(breakpoint, target);
483         }
484     }
485
486     _updateDOMBreakpoint(breakpoint, target)
487     {
488         console.assert(target.DOMDebuggerAgent);
489
490         if (!breakpoint.domNodeIdentifier)
491             return;
492
493         if (breakpoint.disabled)
494             target.DOMDebuggerAgent.removeDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
495         else {
496             if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
497                 WI.debuggerManager.breakpointsEnabled = true;
498
499             target.DOMDebuggerAgent.setDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
500         }
501     }
502
503     _updateEventBreakpoint(breakpoint, target)
504     {
505         console.assert(target.DOMDebuggerAgent);
506
507         // Compatibility (iOS 12): DOMDebuggerAgent.removeEventBreakpoint did not exist.
508         if (!WI.DOMDebuggerManager.supportsEventBreakpoints()) {
509             console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
510             if (breakpoint.disabled)
511                 target.DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName);
512             else {
513                 if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
514                     WI.debuggerManager.breakpointsEnabled = true;
515
516                 target.DOMDebuggerAgent.setEventListenerBreakpoint(breakpoint.eventName);
517             }
518             return;
519         }
520
521         if (breakpoint.disabled)
522             target.DOMDebuggerAgent.removeEventBreakpoint(breakpoint.type, breakpoint.eventName);
523         else {
524             if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
525                 WI.debuggerManager.breakpointsEnabled = true;
526
527             target.DOMDebuggerAgent.setEventBreakpoint(breakpoint.type, breakpoint.eventName);
528         }
529     }
530
531     _updateURLBreakpoint(breakpoint, target)
532     {
533         console.assert(target.DOMDebuggerAgent);
534
535         // Compatibility (iOS 12.1): DOMDebuggerAgent.removeURLBreakpoint did not exist.
536         if (!WI.DOMDebuggerManager.supportsURLBreakpoints()) {
537             if (breakpoint.disabled)
538                 target.DOMDebuggerAgent.removeXHRBreakpoint(breakpoint.url);
539             else {
540                 if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
541                     WI.debuggerManager.breakpointsEnabled = true;
542
543                 let isRegex = breakpoint.type === WI.URLBreakpoint.Type.RegularExpression;
544                 target.DOMDebuggerAgent.setXHRBreakpoint(breakpoint.url, isRegex);
545             }
546             return;
547         }
548
549         if (breakpoint.disabled)
550             target.DOMDebuggerAgent.removeURLBreakpoint(breakpoint.url);
551         else {
552             if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
553                 WI.debuggerManager.breakpointsEnabled = true;
554
555             let isRegex = breakpoint.type === WI.URLBreakpoint.Type.RegularExpression;
556             target.DOMDebuggerAgent.setURLBreakpoint(breakpoint.url, isRegex);
557         }
558     }
559
560     _handleDOMBreakpointDisabledStateChanged(event)
561     {
562         let breakpoint = event.target;
563         let target = WI.assumingMainTarget();
564         if (target && target.DOMDebuggerAgent)
565             this._updateDOMBreakpoint(breakpoint, target);
566
567         if (!this._restoringBreakpoints)
568             WI.objectStores.domBreakpoints.putObject(breakpoint);
569     }
570
571     _handleEventBreakpointDisabledStateChanged(event)
572     {
573         let breakpoint = event.target;
574
575         // Specific event listener breakpoints are handled by `DOMManager`.
576         if (breakpoint.eventListener)
577             return;
578
579         for (let target of WI.targets) {
580             if (target.DOMDebuggerAgent)
581                 this._updateEventBreakpoint(breakpoint, target);
582         }
583
584         if (!this._restoringBreakpoints)
585             WI.objectStores.eventBreakpoints.putObject(breakpoint);
586     }
587
588     _handleURLBreakpointDisabledStateChanged(event)
589     {
590         let breakpoint = event.target;
591
592         if (breakpoint === this._allRequestsBreakpoint)
593             this._allRequestsBreakpointEnabledSetting.value = !breakpoint.disabled;
594
595         for (let target of WI.targets) {
596             if (target.DOMDebuggerAgent)
597                 this._updateURLBreakpoint(breakpoint, target);
598         }
599
600         if (!this._restoringBreakpoints)
601             WI.objectStores.urlBreakpoints.putObject(breakpoint);
602     }
603
604     _childFrameWasRemoved(event)
605     {
606         let frame = event.data.childFrame;
607         this._detachBreakpointsForFrame(frame);
608     }
609
610     _mainFrameDidChange(event)
611     {
612         this._speculativelyResolveDOMBreakpointsForURL(WI.networkManager.mainFrame.url);
613     }
614
615     _mainResourceDidChange(event)
616     {
617         let frame = event.target;
618         if (frame.isMainFrame()) {
619             for (let breakpoint of this._domBreakpointURLMap.values())
620                 breakpoint.domNodeIdentifier = null;
621
622             this._domBreakpointFrameIdentifierMap.clear();
623         } else
624             this._detachBreakpointsForFrame(frame);
625
626         this._speculativelyResolveDOMBreakpointsForURL(frame.url);
627     }
628
629     _nodeInserted(event)
630     {
631         let node = event.data.node;
632         if (node.nodeType() !== Node.ELEMENT_NODE || !node.frame)
633             return;
634
635         let url = node.frame.url;
636         let breakpoints = this._domBreakpointURLMap.get(url);
637         if (!breakpoints)
638             return;
639
640         for (let breakpoint of breakpoints) {
641             if (breakpoint.domNodeIdentifier)
642                 continue;
643
644             if (breakpoint.path !== node.path())
645                 continue;
646
647             this._resolveDOMBreakpoint(breakpoint, node.id);
648         }
649     }
650
651     _nodeRemoved(event)
652     {
653         let node = event.data.node;
654         if (node.nodeType() !== Node.ELEMENT_NODE || !node.frame)
655             return;
656
657         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame.id);
658         if (!domBreakpointNodeIdentifierMap)
659             return;
660
661         let breakpoints = domBreakpointNodeIdentifierMap.get(node.id);
662         if (!breakpoints)
663             return;
664
665         domBreakpointNodeIdentifierMap.delete(node.id);
666
667         if (!domBreakpointNodeIdentifierMap.size)
668             this._domBreakpointFrameIdentifierMap.delete(node.frame.id);
669
670         for (let breakpoint of breakpoints)
671             breakpoint.domNodeIdentifier = null;
672     }
673 };
674
675 WI.DOMDebuggerManager.Event = {
676     DOMBreakpointAdded: "dom-debugger-manager-dom-breakpoint-added",
677     DOMBreakpointRemoved: "dom-debugger-manager-dom-breakpoint-removed",
678     EventBreakpointAdded: "dom-debugger-manager-event-breakpoint-added",
679     EventBreakpointRemoved: "dom-debugger-manager-event-breakpoint-removed",
680     URLBreakpointAdded: "dom-debugger-manager-url-breakpoint-added",
681     URLBreakpointRemoved: "dom-debugger-manager-url-breakpoint-removed",
682 };