2010-08-26 Joseph Pecoraro <joepeck@webkit.org>
[WebKit-https.git] / WebCore / inspector / front-end / DOMAgent.js
1 /*
2  * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2009 Joseph Pecoraro
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 WebInspector.DOMNode = function(doc, payload) {
33     this.ownerDocument = doc;
34
35     this.id = payload.id;
36     this.nodeType = payload.nodeType;
37     this.nodeName = payload.nodeName;
38     this.localName = payload.localName;
39     this._nodeValue = payload.nodeValue;
40     this.textContent = this.nodeValue;
41
42     this.attributes = [];
43     this._attributesMap = {};
44     if (payload.attributes)
45         this._setAttributesPayload(payload.attributes);
46
47     this._childNodeCount = payload.childNodeCount;
48     this.children = null;
49
50     this.nextSibling = null;
51     this.prevSibling = null;
52     this.firstChild = null;
53     this.lastChild = null;
54     this.parentNode = null;
55
56     if (payload.children)
57         this._setChildrenPayload(payload.children);
58
59     this._computedStyle = null;
60     this.style = null;
61     this._matchedCSSRules = [];
62
63     if (this.nodeType === Node.ELEMENT_NODE) {
64         // HTML and BODY from internal iframes should not overwrite top-level ones.
65         if (!this.ownerDocument.documentElement && this.nodeName === "HTML")
66             this.ownerDocument.documentElement = this;
67         if (!this.ownerDocument.body && this.nodeName === "BODY")
68             this.ownerDocument.body = this;
69         if (payload.documentURL)
70             this.documentURL = payload.documentURL;
71     } else if (this.nodeType === Node.DOCUMENT_TYPE_NODE) {
72         this.publicId = payload.publicId;
73         this.systemId = payload.systemId;
74         this.internalSubset = payload.internalSubset;
75     } else if (this.nodeType === Node.DOCUMENT_NODE) {
76         this.documentURL = payload.documentURL;
77     } else if (this.nodeType === Node.ATTRIBUTE_NODE) {
78         this.name = payload.name;
79         this.value = payload.value;
80     }
81 }
82
83 WebInspector.DOMNode.prototype = {
84     hasAttributes: function()
85     {
86         return this.attributes.length > 0;
87     },
88
89     hasChildNodes: function()
90     {
91         return this._childNodeCount > 0;
92     },
93
94     get nodeValue() {
95         return this._nodeValue;
96     },
97
98     set nodeValue(value) {
99         if (this.nodeType != Node.TEXT_NODE)
100             return;
101         var self = this;
102         var callback = function()
103         {
104             self._nodeValue = value;
105             self.textContent = value;
106         };
107         this.ownerDocument._domAgent.setTextNodeValueAsync(this, value, callback);
108     },
109
110     getAttribute: function(name)
111     {
112         var attr = this._attributesMap[name];
113         return attr ? attr.value : undefined;
114     },
115
116     setAttribute: function(name, value)
117     {
118         var self = this;
119         var callback = function()
120         {
121             var attr = self._attributesMap[name];
122             if (attr)
123                 attr.value = value;
124             else
125                 attr = self._addAttribute(name, value);
126         };
127         this.ownerDocument._domAgent.setAttributeAsync(this, name, value, callback);
128     },
129
130     removeAttribute: function(name)
131     {
132         var self = this;
133         var callback = function()
134         {
135             delete self._attributesMap[name];
136             for (var i = 0;  i < self.attributes.length; ++i) {
137                 if (self.attributes[i].name == name) {
138                     self.attributes.splice(i, 1);
139                     break;
140                 }
141             }
142         };
143         this.ownerDocument._domAgent.removeAttributeAsync(this, name, callback);
144     },
145
146     _setAttributesPayload: function(attrs)
147     {
148         this.attributes = [];
149         this._attributesMap = {};
150         for (var i = 0; i < attrs.length; i += 2)
151             this._addAttribute(attrs[i], attrs[i + 1]);
152     },
153
154     _insertChild: function(prev, payload)
155     {
156         var node = new WebInspector.DOMNode(this.ownerDocument, payload);
157         if (!prev) {
158             if (!this.children) {
159                 // First node
160                 this.children = [ node ];
161             } else
162                 this.children.unshift(node);
163         } else
164             this.children.splice(this.children.indexOf(prev) + 1, 0, node);
165         this._renumber();
166         return node;
167     },
168
169     removeChild_: function(node)
170     {
171         this.children.splice(this.children.indexOf(node), 1);
172         node.parentNode = null;
173         this._renumber();
174     },
175
176     _setChildrenPayload: function(payloads)
177     {
178         this.children = [];
179         for (var i = 0; i < payloads.length; ++i) {
180             var payload = payloads[i];
181             var node = new WebInspector.DOMNode(this.ownerDocument, payload);
182             this.children.push(node);
183         }
184         this._renumber();
185     },
186
187     _renumber: function()
188     {
189         this._childNodeCount = this.children.length;
190         if (this._childNodeCount == 0) {
191             this.firstChild = null;
192             this.lastChild = null;
193             return;
194         }
195         this.firstChild = this.children[0];
196         this.lastChild = this.children[this._childNodeCount - 1];
197         for (var i = 0; i < this._childNodeCount; ++i) {
198             var child = this.children[i];
199             child.index = i;
200             child.nextSibling = i + 1 < this._childNodeCount ? this.children[i + 1] : null;
201             child.prevSibling = i - 1 >= 0 ? this.children[i - 1] : null;
202             child.parentNode = this;
203         }
204     },
205
206     _addAttribute: function(name, value)
207     {
208         var attr = {
209             "name": name,
210             "value": value,
211             "_node": this
212         };
213         this._attributesMap[name] = attr;
214         this.attributes.push(attr);
215     }
216 }
217
218 WebInspector.DOMDocument = function(domAgent, defaultView, payload)
219 {
220     WebInspector.DOMNode.call(this, this, payload);
221     this._listeners = {};
222     this._domAgent = domAgent;
223     this.defaultView = defaultView;
224 }
225
226 WebInspector.DOMDocument.prototype = {
227
228     addEventListener: function(name, callback)
229     {
230         var listeners = this._listeners[name];
231         if (!listeners) {
232             listeners = [];
233             this._listeners[name] = listeners;
234         }
235         listeners.push(callback);
236     },
237
238     removeEventListener: function(name, callback)
239     {
240         var listeners = this._listeners[name];
241         if (!listeners)
242             return;
243
244         var index = listeners.indexOf(callback);
245         if (index != -1)
246             listeners.splice(index, 1);
247     },
248
249     _fireDomEvent: function(name, event)
250     {
251         var listeners = this._listeners[name];
252         if (!listeners)
253             return;
254
255         for (var i = 0; i < listeners.length; ++i) {
256             var listener = listeners[i];
257             listener.call(this, event);
258         }
259     }
260 }
261
262 WebInspector.DOMDocument.prototype.__proto__ = WebInspector.DOMNode.prototype;
263
264
265 WebInspector.DOMWindow = function(domAgent)
266 {
267     this._domAgent = domAgent;
268 }
269
270 WebInspector.DOMWindow.prototype = {
271     get document()
272     {
273         return this._domAgent.document;
274     },
275
276     get Node()
277     {
278         return WebInspector.DOMNode;
279     },
280
281     get Element()
282     {
283         return WebInspector.DOMNode;
284     },
285
286     Object: function()
287     {
288     }
289 }
290
291 WebInspector.DOMAgent = function() {
292     this._window = new WebInspector.DOMWindow(this);
293     this._idToDOMNode = null;
294     this.document = null;
295 }
296
297 WebInspector.DOMAgent.prototype = {
298     get domWindow()
299     {
300         return this._window;
301     },
302
303     getChildNodesAsync: function(parent, callback)
304     {
305         var children = parent.children;
306         if (children) {
307             callback(children);
308             return;
309         }
310         function mycallback() {
311             callback(parent.children);
312         }
313         InspectorBackend.getChildNodes(parent.id, mycallback);
314     },
315
316     setAttributeAsync: function(node, name, value, callback)
317     {
318         var mycallback = this._didApplyDomChange.bind(this, node, callback);
319         InspectorBackend.setAttribute(node.id, name, value, mycallback);
320     },
321
322     removeAttributeAsync: function(node, name, callback)
323     {
324         var mycallback = this._didApplyDomChange.bind(this, node, callback);
325         InspectorBackend.removeAttribute(node.id, name, mycallback);
326     },
327
328     setTextNodeValueAsync: function(node, text, callback)
329     {
330         var mycallback = this._didApplyDomChange.bind(this, node, callback);
331         InspectorBackend.setTextNodeValue(node.id, text, mycallback);
332     },
333
334     _didApplyDomChange: function(node, callback, success)
335     {
336         if (!success)
337             return;
338         callback();
339         // TODO(pfeldman): Fix this hack.
340         var elem = WebInspector.panels.elements.treeOutline.findTreeElement(node);
341         if (elem)
342             elem.updateTitle();
343     },
344
345     _attributesUpdated: function(nodeId, attrsArray)
346     {
347         var node = this._idToDOMNode[nodeId];
348         node._setAttributesPayload(attrsArray);
349         var event = {target: node};
350         this.document._fireDomEvent("DOMAttrModified", event);
351     },
352
353     nodeForId: function(nodeId)
354     {
355         return this._idToDOMNode[nodeId];
356     },
357
358     _setDocument: function(payload)
359     {
360         this._idToDOMNode = {};
361         if (payload && "id" in payload) {
362             this.document = new WebInspector.DOMDocument(this, this._window, payload);
363             this._idToDOMNode[payload.id] = this.document;
364             this._bindNodes(this.document.children);
365         } else
366             this.document = null;
367         WebInspector.panels.elements.setDocument(this.document);
368     },
369
370     _setDetachedRoot: function(payload)
371     {
372         var root = new WebInspector.DOMNode(this.document, payload);
373         this._idToDOMNode[payload.id] = root;
374     },
375
376     _setChildNodes: function(parentId, payloads)
377     {
378         var parent = this._idToDOMNode[parentId];
379         parent._setChildrenPayload(payloads);
380         this._bindNodes(parent.children);
381     },
382
383     _bindNodes: function(children)
384     {
385         for (var i = 0; i < children.length; ++i) {
386             var child = children[i];
387             this._idToDOMNode[child.id] = child;
388             if (child.children)
389                 this._bindNodes(child.children);
390         }
391     },
392
393     _childNodeCountUpdated: function(nodeId, newValue)
394     {
395         var node = this._idToDOMNode[nodeId];
396         node._childNodeCount = newValue;
397         var outline = WebInspector.panels.elements.treeOutline;
398         var treeElement = outline.findTreeElement(node);
399         if (treeElement)
400             treeElement.hasChildren = newValue;
401     },
402
403     _childNodeInserted: function(parentId, prevId, payload)
404     {
405         var parent = this._idToDOMNode[parentId];
406         var prev = this._idToDOMNode[prevId];
407         var node = parent._insertChild(prev, payload);
408         this._idToDOMNode[node.id] = node;
409         var event = { target : node, relatedNode : parent };
410         this.document._fireDomEvent("DOMNodeInserted", event);
411     },
412
413     _childNodeRemoved: function(parentId, nodeId)
414     {
415         var parent = this._idToDOMNode[parentId];
416         var node = this._idToDOMNode[nodeId];
417         parent.removeChild_(node);
418         var event = { target : node, relatedNode : parent };
419         this.document._fireDomEvent("DOMNodeRemoved", event);
420         delete this._idToDOMNode[nodeId];
421     }
422 }
423
424 WebInspector.ApplicationCache = {}
425
426 WebInspector.ApplicationCache.getApplicationCachesAsync = function(callback)
427 {
428     function mycallback(applicationCaches)
429     {
430         // FIXME: Currently, this list only returns a single application cache.
431         if (applicationCaches)
432             callback(applicationCaches);
433     }
434
435     InspectorBackend.getApplicationCaches(mycallback);
436 }
437
438 WebInspector.Cookies = {}
439
440 WebInspector.Cookies.getCookiesAsync = function(callback)
441 {
442     function mycallback(cookies, cookiesString)
443     {
444         if (cookiesString)
445             callback(WebInspector.Cookies.buildCookiesFromString(cookiesString), false);
446         else
447             callback(cookies, true);
448     }
449
450     InspectorBackend.getCookies(mycallback);
451 }
452
453 WebInspector.Cookies.buildCookiesFromString = function(rawCookieString)
454 {
455     var rawCookies = rawCookieString.split(/;\s*/);
456     var cookies = [];
457
458     if (!(/^\s*$/.test(rawCookieString))) {
459         for (var i = 0; i < rawCookies.length; ++i) {
460             var cookie = rawCookies[i];
461             var delimIndex = cookie.indexOf("=");
462             var name = cookie.substring(0, delimIndex);
463             var value = cookie.substring(delimIndex + 1);
464             var size = name.length + value.length;
465             cookies.push({ name: name, value: value, size: size });
466         }
467     }
468
469     return cookies;
470 }
471
472 WebInspector.Cookies.cookieMatchesResourceURL = function(cookie, resourceURL)
473 {
474     var match = resourceURL.match(WebInspector.GenericURLRegExp);
475     if (!match)
476         return false;
477     // See WebInspector.URLRegExp for definitions of the group index constants.
478     if (!this.cookieDomainMatchesResourceDomain(cookie.domain, match[2]))
479         return false;
480     var resourcePort = match[3] ? match[3] : undefined;
481     var resourcePath = match[4] ? match[4] : '/';
482     return (resourcePath.indexOf(cookie.path) === 0
483         && (!cookie.port || resourcePort == cookie.port)
484         && (!cookie.secure || match[1].toLowerCase() === 'https'));
485 }
486
487 WebInspector.Cookies.cookieDomainMatchesResourceDomain = function(cookieDomain, resourceDomain)
488 {
489     if (cookieDomain.charAt(0) !== '.')
490         return resourceDomain === cookieDomain;
491     return !!resourceDomain.match(new RegExp("^([^\\.]+\\.)?" + cookieDomain.substring(1).escapeForRegExp() + "$"), "i");
492 }
493
494 WebInspector.EventListeners = {}
495
496 WebInspector.EventListeners.getEventListenersForNodeAsync = function(node, callback)
497 {
498     if (!node)
499         return;
500     InspectorBackend.getEventListenersForNode(node.id, callback);
501 }
502
503 WebInspector.CSSStyleDeclaration = function(payload)
504 {
505     this.id = payload.id;
506     this.parentStyleSheetId = payload.parentStyleSheetId;
507     this.width = payload.width;
508     this.height = payload.height;
509     this.__disabledProperties = {};
510     this.__disabledPropertyValues = {};
511     this.__disabledPropertyPriorities = {};
512     if (payload.disabled) {
513         for (var i = 0; i < payload.disabled.length; ++i) {
514             var property = payload.disabled[i];
515             this.__disabledProperties[property.name] = true;
516             this.__disabledPropertyValues[property.name] = property.value;
517             this.__disabledPropertyPriorities[property.name] = property.priority;
518         }
519     }
520
521     this._shorthandValues = payload.shorthandValues;
522     this._propertyMap = {};
523     this._longhandProperties = {};
524     this.length = payload.properties.length;
525
526     for (var i = 0; i < this.length; ++i) {
527         var property = payload.properties[i];
528         var name = property.name;
529         this[i] = name;
530         this._propertyMap[name] = property;
531
532         // Index longhand properties.
533         if (property.shorthand) {
534             var longhands = this._longhandProperties[property.shorthand];
535             if (!longhands) {
536                 longhands = [];
537                 this._longhandProperties[property.shorthand] = longhands;
538             }
539             longhands.push(name);
540         }
541     }
542 }
543
544 WebInspector.CSSStyleDeclaration.parseStyle = function(payload)
545 {
546     return new WebInspector.CSSStyleDeclaration(payload);
547 }
548
549 WebInspector.CSSStyleDeclaration.parseRule = function(payload)
550 {
551     var rule = {};
552     rule.id = payload.id;
553     rule.selectorText = payload.selectorText;
554     rule.style = new WebInspector.CSSStyleDeclaration(payload.style);
555     rule.style.parentRule = rule;
556     rule.isUserAgent = payload.isUserAgent;
557     rule.isUser = payload.isUser;
558     rule.isViaInspector = payload.isViaInspector;
559     rule.sourceLine = payload.sourceLine;
560     rule.documentURL = payload.documentURL;
561     if (payload.parentStyleSheet)
562         rule.parentStyleSheet = { href: payload.parentStyleSheet.href };
563
564     return rule;
565 }
566
567 WebInspector.CSSStyleDeclaration.prototype = {
568     getPropertyValue: function(name)
569     {
570         var property = this._propertyMap[name];
571         return property ? property.value : "";
572     },
573
574     getPropertyPriority: function(name)
575     {
576         var property = this._propertyMap[name];
577         return property ? property.priority : "";
578     },
579
580     getPropertyShorthand: function(name)
581     {
582         var property = this._propertyMap[name];
583         return property ? property.shorthand : "";
584     },
585
586     isPropertyImplicit: function(name)
587     {
588         var property = this._propertyMap[name];
589         return property ? property.implicit : "";
590     },
591
592     styleTextWithShorthands: function()
593     {
594         var cssText = "";
595         var foundProperties = {};
596         for (var i = 0; i < this.length; ++i) {
597             var individualProperty = this[i];
598             var shorthandProperty = this.getPropertyShorthand(individualProperty);
599             var propertyName = (shorthandProperty || individualProperty);
600
601             if (propertyName in foundProperties)
602                 continue;
603
604             if (shorthandProperty) {
605                 var value = this.getShorthandValue(shorthandProperty);
606                 var priority = this.getShorthandPriority(shorthandProperty);
607             } else {
608                 var value = this.getPropertyValue(individualProperty);
609                 var priority = this.getPropertyPriority(individualProperty);
610             }
611
612             foundProperties[propertyName] = true;
613
614             cssText += propertyName + ": " + value;
615             if (priority)
616                 cssText += " !" + priority;
617             cssText += "; ";
618         }
619
620         return cssText;
621     },
622
623     getLonghandProperties: function(name)
624     {
625         return this._longhandProperties[name] || [];
626     },
627
628     getShorthandValue: function(shorthandProperty)
629     {
630         return this._shorthandValues[shorthandProperty];
631     },
632
633     getShorthandPriority: function(shorthandProperty)
634     {
635         var priority = this.getPropertyPriority(shorthandProperty);
636         if (priority)
637             return priority;
638
639         var longhands = this._longhandProperties[shorthandProperty];
640         return longhands ? this.getPropertyPriority(longhands[0]) : null;
641     }
642 }
643
644 WebInspector.attributesUpdated = function()
645 {
646     this.domAgent._attributesUpdated.apply(this.domAgent, arguments);
647 }
648
649 WebInspector.setDocument = function()
650 {
651     this.domAgent._setDocument.apply(this.domAgent, arguments);
652 }
653
654 WebInspector.setDetachedRoot = function()
655 {
656     this.domAgent._setDetachedRoot.apply(this.domAgent, arguments);
657 }
658
659 WebInspector.setChildNodes = function()
660 {
661     this.domAgent._setChildNodes.apply(this.domAgent, arguments);
662 }
663
664 WebInspector.childNodeCountUpdated = function()
665 {
666     this.domAgent._childNodeCountUpdated.apply(this.domAgent, arguments);
667 }
668
669 WebInspector.childNodeInserted = function()
670 {
671     this.domAgent._childNodeInserted.apply(this.domAgent, arguments);
672 }
673
674 WebInspector.childNodeRemoved = function()
675 {
676     this.domAgent._childNodeRemoved.apply(this.domAgent, arguments);
677 }
678
679 WebInspector.DOMBreakpointManager = function()
680 {
681     this._breakpoints = {};
682 }
683
684 WebInspector.DOMBreakpointManager.prototype = {
685     setBreakpoint: function(node, type)
686     {
687         if (!(node.id in this._breakpoints))
688             this._breakpoints[node.id] = {};
689         else if (type in this._breakpoints[node.id])
690             return;
691
692         var breakpoint = new WebInspector.DOMBreakpoint(node, type);
693         this._breakpoints[node.id][type] = breakpoint;
694         breakpoint.addEventListener("removed", this._breakpointRemoved, this);
695
696         this.dispatchEventToListeners("dom-breakpoint-added", breakpoint);
697     },
698
699     findBreakpoint: function(nodeId, type)
700     {
701         var nodeBreakpoints = this._breakpoints[nodeId];
702         if (nodeBreakpoints && type in nodeBreakpoints)
703             return nodeBreakpoints[type];
704     },
705
706     removeBreakpointsForNode: function(node)
707     {
708         var nodeBreakpoints = this._breakpoints[node.id];
709         for (var type in nodeBreakpoints)
710             nodeBreakpoints[type].remove();
711     },
712
713     _breakpointRemoved: function(event)
714     {
715         var breakpoint = event.target;
716
717         var nodeBreakpoints = this._breakpoints[breakpoint.node.id];
718         delete nodeBreakpoints[breakpoint.type];
719         for (var type in nodeBreakpoints)
720             return;
721         delete this._breakpoints[breakpoint.node.id];
722     }
723 }
724
725 WebInspector.DOMBreakpointManager.prototype.__proto__ = WebInspector.Object.prototype;
726
727 WebInspector.DOMBreakpoint = function(node, type)
728 {
729     this.node = node;
730     this.type = type;
731     this._enabled = true;
732
733     InspectorBackend.setDOMBreakpoint(this.node.id, this.type);
734 }
735
736 WebInspector.DOMBreakpoint.Types = {
737     SubtreeModified: 0,
738     AttributeModified: 1,
739     NodeRemoved: 2
740 };
741
742 WebInspector.DOMBreakpoint.labelForType = function(type)
743 {
744     if (!WebInspector.DOMBreakpoint._labels) {
745         WebInspector.DOMBreakpoint._labels = {};
746         WebInspector.DOMBreakpoint._labels[WebInspector.DOMBreakpoint.Types.SubtreeModified] = WebInspector.UIString("Subtree Modified");
747         WebInspector.DOMBreakpoint._labels[WebInspector.DOMBreakpoint.Types.AttributeModified] = WebInspector.UIString("Attribute Modified");
748         WebInspector.DOMBreakpoint._labels[WebInspector.DOMBreakpoint.Types.NodeRemoved] = WebInspector.UIString("Node Removed");
749     }
750     return WebInspector.DOMBreakpoint._labels[type];
751 }
752
753 WebInspector.DOMBreakpoint.contextMenuLabelForType = function(type)
754 {
755     if (!WebInspector.DOMBreakpoint._contextMenuLabels) {
756         WebInspector.DOMBreakpoint._contextMenuLabels = {};
757         WebInspector.DOMBreakpoint._contextMenuLabels[WebInspector.DOMBreakpoint.Types.SubtreeModified] = WebInspector.UIString("Break on Subtree Modifications");
758         WebInspector.DOMBreakpoint._contextMenuLabels[WebInspector.DOMBreakpoint.Types.AttributeModified] = WebInspector.UIString("Break on Attributes Modifications");
759         WebInspector.DOMBreakpoint._contextMenuLabels[WebInspector.DOMBreakpoint.Types.NodeRemoved] = WebInspector.UIString("Break on Node Removal");
760     }
761     return WebInspector.DOMBreakpoint._contextMenuLabels[type];
762 }
763
764 WebInspector.DOMBreakpoint.prototype = {
765     get enabled()
766     {
767         return this._enabled;
768     },
769
770     set enabled(enabled)
771     {
772         if (this._enabled === enabled)
773             return;
774
775         this._enabled = enabled;
776         if (this._enabled)
777             InspectorBackend.setDOMBreakpoint(this.node.id, this.type);
778         else
779             InspectorBackend.removeDOMBreakpoint(this.node.id, this.type);
780
781         this.dispatchEventToListeners("enable-changed");
782     },
783
784     remove: function()
785     {
786         if (this._enabled)
787             InspectorBackend.removeDOMBreakpoint(this.node.id, this.type);
788         this.dispatchEventToListeners("removed");
789     }
790 }
791
792 WebInspector.DOMBreakpoint.prototype.__proto__ = WebInspector.Object.prototype;
793