summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Models
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Models
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Models')
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/AnalyzerMessage.js46
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ApplicationCacheFrame.js56
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ApplicationCacheManifest.js38
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/BackForwardEntry.js157
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Branch.js146
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Breakpoint.js364
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/BreakpointAction.js85
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js298
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSKeywordCompletions.js1035
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSMedia.js51
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSProperty.js259
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSRule.js257
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSSelector.js64
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js362
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CSSStyleSheet.js226
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CallFrame.js260
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CallingContextTree.js176
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CallingContextTreeNode.js244
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Collection.js119
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CollectionEntry.js56
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CollectionEntryPreview.js56
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Color.js762
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ConsoleCommandResultMessage.js67
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js138
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ContentFlow.js105
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/CookieStorageObject.js68
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DOMNode.js833
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js985
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DOMSearchMatchObject.js172
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DOMStorageObject.js119
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DOMTree.js309
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DatabaseObject.js95
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DatabaseTableObject.js50
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DebuggerDashboard.js28
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DebuggerData.js155
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/DefaultDashboard.js269
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ExecutionContext.js50
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ExecutionContextList.js72
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/FPSInstrument.js49
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Frame.js509
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/GarbageCollection.js65
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Geometry.js571
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Gradient.js392
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js86
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js43
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js178
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/IndexedDatabase.js59
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStore.js66
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStoreIndex.js63
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Instrument.js104
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/IssueMessage.js235
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/KeyboardShortcut.js269
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/LayoutInstrument.js34
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/LayoutTimelineRecord.js110
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/LazySourceCodeLocation.js112
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/LineWidget.js61
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/LogObject.js41
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/MemoryCategory.js46
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/MemoryInstrument.js61
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/MemoryPressureEvent.js66
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/MemoryTimeline.js53
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js87
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js2232
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/NetworkInstrument.js44
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/NetworkTimeline.js56
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ObjectPreview.js86
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Probe.js108
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ProbeSet.js148
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ProbeSetDataFrame.js101
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ProbeSetDataTable.js132
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Profile.js52
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ProfileNode.js278
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ProfileNodeCall.js66
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/PropertyDescriptor.js119
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/PropertyPath.js268
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/PropertyPreview.js64
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/RenderingFrameTimelineRecord.js148
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ReplayDashboard.js28
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ReplaySession.js90
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js67
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Resource.js804
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ResourceCollection.js199
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ResourceQueryMatch.js48
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ResourceQueryResult.js149
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js74
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ResourceTimingData.js107
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Revision.js47
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ScopeChainNode.js79
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Script.js303
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js61
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ScriptSyntaxTree.js1146
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js418
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCode.js226
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js464
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCodePosition.js40
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js76
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCodeSearchMatchObject.js66
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCodeTextRange.js123
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCodeTimeline.js65
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceMap.js286
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceMapResource.js178
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/StackTrace.js175
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/StructureDescription.js62
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TextMarker.js89
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TextRange.js113
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Timeline.js136
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TimelineMarker.js80
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TimelineRange.js41
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js169
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js375
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TypeDescription.js66
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/TypeSet.js132
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/WrappedPromise.js71
113 files changed, 22547 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Models/AnalyzerMessage.js b/Source/WebInspectorUI/UserInterface/Models/AnalyzerMessage.js
new file mode 100644
index 000000000..490bb7909
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/AnalyzerMessage.js
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.AnalyzerMessage = class AnalyzerMessage extends WebInspector.Object
+{
+ constructor(sourceCodeLocation, text, ruleIdentifier)
+ {
+ super();
+
+ console.assert(sourceCodeLocation instanceof WebInspector.SourceCodeLocation);
+ console.assert(typeof text === "string");
+
+ this._sourceCodeLocation = sourceCodeLocation;
+ this._text = text;
+ this._ruleIdentifier = ruleIdentifier;
+ }
+
+ // Public
+
+ get sourceCodeLocation() { return this._sourceCodeLocation; }
+ get sourceCode() { return this._sourceCodeLocation.sourceCode; }
+ get text() { return this._text; }
+ get ruleIdentifier() { return this._ruleIdentifier; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ApplicationCacheFrame.js b/Source/WebInspectorUI/UserInterface/Models/ApplicationCacheFrame.js
new file mode 100644
index 000000000..068202243
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ApplicationCacheFrame.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ApplicationCacheFrame = class ApplicationCacheFrame extends WebInspector.Object
+{
+ constructor(frame, manifest, status)
+ {
+ super();
+
+ console.assert(frame instanceof WebInspector.Frame);
+ console.assert(manifest instanceof WebInspector.ApplicationCacheManifest);
+
+ this._frame = frame;
+ this._manifest = manifest;
+ this._status = status;
+ }
+
+ // Public
+
+ get frame() { return this._frame; }
+ get manifest() { return this._manifest; }
+ get status() { return this._status; }
+ set status(status) { this._status = status; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.ApplicationCacheFrame.FrameURLCookieKey] = this.frame.url;
+ cookie[WebInspector.ApplicationCacheFrame.ManifestURLCookieKey] = this.manifest.manifestURL;
+ }
+};
+
+WebInspector.ApplicationCacheFrame.TypeIdentifier = "application-cache-frame";
+WebInspector.ApplicationCacheFrame.FrameURLCookieKey = "application-cache-frame-url";
+WebInspector.ApplicationCacheFrame.ManifestURLCookieKey = "application-cache-frame-manifest-url";
diff --git a/Source/WebInspectorUI/UserInterface/Models/ApplicationCacheManifest.js b/Source/WebInspectorUI/UserInterface/Models/ApplicationCacheManifest.js
new file mode 100644
index 000000000..000241d48
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ApplicationCacheManifest.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ApplicationCacheManifest = class ApplicationCacheManifest extends WebInspector.Object
+{
+ constructor(manifestURL)
+ {
+ super();
+
+ this._manifestURL = manifestURL;
+ }
+
+ // Public
+
+ get manifestURL() { return this._manifestURL; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/BackForwardEntry.js b/Source/WebInspectorUI/UserInterface/Models/BackForwardEntry.js
new file mode 100644
index 000000000..2c5ec3dab
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/BackForwardEntry.js
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.BackForwardEntry = class BackForwardEntry extends WebInspector.Object
+{
+ constructor(contentView, cookie)
+ {
+ super();
+
+ this._contentView = contentView;
+
+ // ContentViews may be shared across multiple ContentViewContainers.
+ // A BackForwardEntry containing a tombstone doesn't actually have
+ // the real ContentView, and so should not close it.
+ this._tombstone = false;
+
+ // Cookies are compared with Object.shallowEqual, so should not store objects or arrays.
+ this._cookie = cookie || {};
+ this._scrollPositions = [];
+
+ contentView.saveToCookie(this._cookie);
+ }
+
+ // Public
+
+ makeCopy(newCookie)
+ {
+ let copy = new WebInspector.BackForwardEntry(this._contentView, newCookie || this.cookie);
+ copy._tombstone = this._tombstone;
+ copy._scrollPositions = this._scrollPositions.slice();
+ return copy;
+ }
+
+ get contentView()
+ {
+ return this._contentView;
+ }
+
+ get cookie()
+ {
+ // Cookies are immutable; they represent a specific navigation action.
+ return Object.shallowCopy(this._cookie);
+ }
+
+ get tombstone()
+ {
+ return this._tombstone;
+ }
+
+ set tombstone(tombstone)
+ {
+ this._tombstone = tombstone;
+ }
+
+ prepareToShow(shouldCallShown)
+ {
+ console.assert(!this._tombstone, "Should not be calling shown on a tombstone");
+
+ this._restoreFromCookie();
+
+ this.contentView.visible = true;
+ if (shouldCallShown)
+ this.contentView.shown();
+ this.contentView.needsLayout();
+ }
+
+ prepareToHide()
+ {
+ console.assert(!this._tombstone, "Should not be calling hidden on a tombstone");
+
+ this.contentView.visible = false;
+ this.contentView.hidden();
+
+ this._saveScrollPositions();
+ }
+
+ isEqual(other)
+ {
+ if (!other)
+ return false;
+ return this._contentView === other._contentView && Object.shallowEqual(this._cookie, other._cookie);
+ }
+
+ // Private
+
+ _restoreFromCookie()
+ {
+ this._restoreScrollPositions();
+ this.contentView.restoreFromCookie(this.cookie);
+ }
+
+ _restoreScrollPositions()
+ {
+ // If no scroll positions are saved, do nothing.
+ if (!this._scrollPositions.length)
+ return;
+
+ var scrollableElements = this.contentView.scrollableElements || [];
+ console.assert(this._scrollPositions.length === scrollableElements.length);
+
+ for (var i = 0; i < scrollableElements.length; ++i) {
+ var position = this._scrollPositions[i];
+ var element = scrollableElements[i];
+ if (!element)
+ continue;
+
+ // Restore the top scroll position by either scrolling to the bottom or to the saved position.
+ element.scrollTop = position.isScrolledToBottom ? element.scrollHeight : position.scrollTop;
+
+ // Don't restore the left scroll position when scrolled to the bottom. This way the when content changes
+ // the user won't be left in a weird horizontal position.
+ element.scrollLeft = position.isScrolledToBottom ? 0 : position.scrollLeft;
+ }
+ }
+
+ _saveScrollPositions()
+ {
+ var scrollableElements = this.contentView.scrollableElements || [];
+ var scrollPositions = [];
+ for (var i = 0; i < scrollableElements.length; ++i) {
+ var element = scrollableElements[i];
+ if (!element)
+ continue;
+
+ let position = {scrollTop: element.scrollTop, scrollLeft: element.scrollLeft};
+ if (this.contentView.shouldKeepElementsScrolledToBottom)
+ position.isScrolledToBottom = element.isScrolledToBottom();
+
+ scrollPositions.push(position);
+ }
+
+ this._scrollPositions = scrollPositions;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Branch.js b/Source/WebInspectorUI/UserInterface/Models/Branch.js
new file mode 100644
index 000000000..829764baa
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Branch.js
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Branch = class Branch extends WebInspector.Object
+{
+ constructor(displayName, revisions, locked)
+ {
+ super();
+
+ console.assert(displayName);
+
+ this._displayName = displayName;
+ this._revisions = revisions instanceof Array ? revisions.slice() : [];
+ this._locked = locked || false;
+ }
+
+ // Public
+
+ get displayName()
+ {
+ return this._displayName;
+ }
+
+ set displayName(displayName)
+ {
+ console.assert(displayName);
+ if (!displayName)
+ return;
+
+ this._displayName = displayName;
+ }
+
+ get revisions()
+ {
+ return this._revisions;
+ }
+
+ get locked()
+ {
+ return this._locked;
+ }
+
+ revisionForRepresentedObject(representedObject, doNotCreateIfNeeded)
+ {
+ for (var i = 0; i < this._revisions.length; ++i) {
+ var revision = this._revisions[i];
+ if (revision instanceof WebInspector.SourceCodeRevision && revision.sourceCode === representedObject)
+ return revision;
+ }
+
+ if (doNotCreateIfNeeded)
+ return null;
+
+ if (representedObject instanceof WebInspector.SourceCode) {
+ var revision = representedObject.originalRevision.copy();
+ representedObject.currentRevision = revision;
+ this.addRevision(revision);
+ return revision;
+ }
+
+ return null;
+ }
+
+ addRevision(revision)
+ {
+ console.assert(revision instanceof WebInspector.Revision);
+
+ if (this._locked)
+ return;
+
+ if (this._revisions.includes(revision))
+ return;
+
+ this._revisions.push(revision);
+ }
+
+ removeRevision(revision)
+ {
+ console.assert(revision instanceof WebInspector.Revision);
+
+ if (this._locked)
+ return;
+
+ this._revisions.remove(revision);
+ }
+
+ reset()
+ {
+ if (this._locked)
+ return;
+
+ this._revisions = [];
+ }
+
+ fork(displayName)
+ {
+ var copiedRevisions = this._revisions.map(function(revision) { return revision.copy(); });
+ return new WebInspector.Branch(displayName, copiedRevisions);
+ }
+
+ apply()
+ {
+ for (var i = 0; i < this._revisions.length; ++i)
+ this._revisions[i].apply();
+ }
+
+ revert()
+ {
+ for (var i = this._revisions.length - 1; i >= 0; --i)
+ this._revisions[i].revert();
+ }
+
+ lock()
+ {
+ console.assert(!this._locked);
+ this._locked = true;
+ }
+
+ unlock()
+ {
+ console.assert(this._locked);
+ this._locked = false;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Breakpoint.js b/Source/WebInspectorUI/UserInterface/Models/Breakpoint.js
new file mode 100644
index 000000000..03f85def1
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Breakpoint.js
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Breakpoint = class Breakpoint extends WebInspector.Object
+{
+ constructor(sourceCodeLocationOrInfo, disabled, condition)
+ {
+ super();
+
+ if (sourceCodeLocationOrInfo instanceof WebInspector.SourceCodeLocation) {
+ var sourceCode = sourceCodeLocationOrInfo.sourceCode;
+ var contentIdentifier = sourceCode ? sourceCode.contentIdentifier : null;
+ var scriptIdentifier = sourceCode instanceof WebInspector.Script ? sourceCode.id : null;
+ var target = sourceCode instanceof WebInspector.Script ? sourceCode.target : null;
+ var location = sourceCodeLocationOrInfo;
+ } else if (sourceCodeLocationOrInfo && typeof sourceCodeLocationOrInfo === "object") {
+ // The 'url' fallback is for transitioning from older frontends and should be removed.
+ var contentIdentifier = sourceCodeLocationOrInfo.contentIdentifier || sourceCodeLocationOrInfo.url;
+ var lineNumber = sourceCodeLocationOrInfo.lineNumber || 0;
+ var columnNumber = sourceCodeLocationOrInfo.columnNumber || 0;
+ var location = new WebInspector.SourceCodeLocation(null, lineNumber, columnNumber);
+ var ignoreCount = sourceCodeLocationOrInfo.ignoreCount || 0;
+ var autoContinue = sourceCodeLocationOrInfo.autoContinue || false;
+ var actions = sourceCodeLocationOrInfo.actions || [];
+ for (var i = 0; i < actions.length; ++i)
+ actions[i] = new WebInspector.BreakpointAction(this, actions[i]);
+ disabled = sourceCodeLocationOrInfo.disabled;
+ condition = sourceCodeLocationOrInfo.condition;
+ } else
+ console.error("Unexpected type passed to WebInspector.Breakpoint", sourceCodeLocationOrInfo);
+
+ this._id = null;
+ this._contentIdentifier = contentIdentifier || null;
+ this._scriptIdentifier = scriptIdentifier || null;
+ this._target = target || null;
+ this._disabled = disabled || false;
+ this._condition = condition || "";
+ this._ignoreCount = ignoreCount || 0;
+ this._autoContinue = autoContinue || false;
+ this._actions = actions || [];
+ this._resolved = false;
+
+ this._sourceCodeLocation = location;
+ this._sourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._sourceCodeLocationLocationChanged, this);
+ this._sourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, this._sourceCodeLocationDisplayLocationChanged, this);
+ }
+
+ // Public
+
+ get identifier()
+ {
+ return this._id;
+ }
+
+ set identifier(id)
+ {
+ this._id = id || null;
+ }
+
+ get contentIdentifier()
+ {
+ return this._contentIdentifier;
+ }
+
+ get scriptIdentifier()
+ {
+ return this._scriptIdentifier;
+ }
+
+ get target()
+ {
+ return this._target;
+ }
+
+ get sourceCodeLocation()
+ {
+ return this._sourceCodeLocation;
+ }
+
+ get resolved()
+ {
+ return this._resolved;
+ }
+
+ set resolved(resolved)
+ {
+ if (this._resolved === resolved)
+ return;
+
+ function isSpecialBreakpoint()
+ {
+ return this._sourceCodeLocation.isEqual(new WebInspector.SourceCodeLocation(null, Infinity, Infinity));
+ }
+
+ console.assert(!resolved || this._sourceCodeLocation.sourceCode || isSpecialBreakpoint.call(this), "Breakpoints must have a SourceCode to be resolved.", this);
+
+ this._resolved = resolved || false;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);
+ }
+
+ get disabled()
+ {
+ return this._disabled;
+ }
+
+ set disabled(disabled)
+ {
+ if (this._disabled === disabled)
+ return;
+
+ this._disabled = disabled || false;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.DisabledStateDidChange);
+ }
+
+ get condition()
+ {
+ return this._condition;
+ }
+
+ set condition(condition)
+ {
+ if (this._condition === condition)
+ return;
+
+ this._condition = condition;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ConditionDidChange);
+ }
+
+ get ignoreCount()
+ {
+ return this._ignoreCount;
+ }
+
+ set ignoreCount(ignoreCount)
+ {
+ console.assert(ignoreCount >= 0, "Ignore count cannot be negative.");
+ if (ignoreCount < 0)
+ return;
+
+ if (this._ignoreCount === ignoreCount)
+ return;
+
+ this._ignoreCount = ignoreCount;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.IgnoreCountDidChange);
+ }
+
+ get autoContinue()
+ {
+ return this._autoContinue;
+ }
+
+ set autoContinue(cont)
+ {
+ if (this._autoContinue === cont)
+ return;
+
+ this._autoContinue = cont;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.AutoContinueDidChange);
+ }
+
+ get actions()
+ {
+ return this._actions;
+ }
+
+ get options()
+ {
+ return {
+ condition: this._condition,
+ ignoreCount: this._ignoreCount,
+ actions: this._serializableActions(),
+ autoContinue: this._autoContinue
+ };
+ }
+
+ get info()
+ {
+ // The id, scriptIdentifier, target, and resolved state are tied to the current session, so don't include them for serialization.
+ return {
+ contentIdentifier: this._contentIdentifier,
+ lineNumber: this._sourceCodeLocation.lineNumber,
+ columnNumber: this._sourceCodeLocation.columnNumber,
+ disabled: this._disabled,
+ condition: this._condition,
+ ignoreCount: this._ignoreCount,
+ actions: this._serializableActions(),
+ autoContinue: this._autoContinue
+ };
+ }
+
+ get probeActions()
+ {
+ return this._actions.filter(function(action) {
+ return action.type === WebInspector.BreakpointAction.Type.Probe;
+ });
+ }
+
+ cycleToNextMode()
+ {
+ if (this.disabled) {
+ // When cycling, clear auto-continue when going from disabled to enabled.
+ this.autoContinue = false;
+ this.disabled = false;
+ return;
+ }
+
+ if (this.autoContinue) {
+ this.disabled = true;
+ return;
+ }
+
+ if (this.actions.length) {
+ this.autoContinue = true;
+ return;
+ }
+
+ this.disabled = true;
+ }
+
+ createAction(type, precedingAction, data)
+ {
+ var newAction = new WebInspector.BreakpointAction(this, type, data || null);
+
+ if (!precedingAction)
+ this._actions.push(newAction);
+ else {
+ var index = this._actions.indexOf(precedingAction);
+ console.assert(index !== -1);
+ if (index === -1)
+ this._actions.push(newAction);
+ else
+ this._actions.splice(index + 1, 0, newAction);
+ }
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
+
+ return newAction;
+ }
+
+ recreateAction(type, actionToReplace)
+ {
+ var newAction = new WebInspector.BreakpointAction(this, type, null);
+
+ var index = this._actions.indexOf(actionToReplace);
+ console.assert(index !== -1);
+ if (index === -1)
+ return null;
+
+ this._actions[index] = newAction;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
+
+ return newAction;
+ }
+
+ removeAction(action)
+ {
+ var index = this._actions.indexOf(action);
+ console.assert(index !== -1);
+ if (index === -1)
+ return;
+
+ this._actions.splice(index, 1);
+
+ if (!this._actions.length)
+ this.autoContinue = false;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
+ }
+
+ clearActions(type)
+ {
+ if (!type)
+ this._actions = [];
+ else
+ this._actions = this._actions.filter(function(action) { return action.type !== type; });
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.Breakpoint.ContentIdentifierCookieKey] = this.contentIdentifier;
+ cookie[WebInspector.Breakpoint.LineNumberCookieKey] = this.sourceCodeLocation.lineNumber;
+ cookie[WebInspector.Breakpoint.ColumnNumberCookieKey] = this.sourceCodeLocation.columnNumber;
+ }
+
+ // Protected (Called by BreakpointAction)
+
+ breakpointActionDidChange(action)
+ {
+ var index = this._actions.indexOf(action);
+ console.assert(index !== -1);
+ if (index === -1)
+ return;
+
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
+ }
+
+ // Private
+
+ _serializableActions()
+ {
+ var actions = [];
+ for (var i = 0; i < this._actions.length; ++i)
+ actions.push(this._actions[i].info);
+ return actions;
+ }
+
+ _sourceCodeLocationLocationChanged(event)
+ {
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.LocationDidChange, event.data);
+ }
+
+ _sourceCodeLocationDisplayLocationChanged(event)
+ {
+ this.dispatchEventToListeners(WebInspector.Breakpoint.Event.DisplayLocationDidChange, event.data);
+ }
+};
+
+WebInspector.Breakpoint.DefaultBreakpointActionType = WebInspector.BreakpointAction.Type.Log;
+
+WebInspector.Breakpoint.TypeIdentifier = "breakpoint";
+WebInspector.Breakpoint.ContentIdentifierCookieKey = "breakpoint-content-identifier";
+WebInspector.Breakpoint.LineNumberCookieKey = "breakpoint-line-number";
+WebInspector.Breakpoint.ColumnNumberCookieKey = "breakpoint-column-number";
+
+WebInspector.Breakpoint.Event = {
+ DisabledStateDidChange: "breakpoint-disabled-state-did-change",
+ ResolvedStateDidChange: "breakpoint-resolved-state-did-change",
+ ConditionDidChange: "breakpoint-condition-did-change",
+ IgnoreCountDidChange: "breakpoint-ignore-count-did-change",
+ ActionsDidChange: "breakpoint-actions-did-change",
+ AutoContinueDidChange: "breakpoint-auto-continue-did-change",
+ LocationDidChange: "breakpoint-location-did-change",
+ DisplayLocationDidChange: "breakpoint-display-location-did-change",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/BreakpointAction.js b/Source/WebInspectorUI/UserInterface/Models/BreakpointAction.js
new file mode 100644
index 000000000..71249a435
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/BreakpointAction.js
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.BreakpointAction = class BreakpointAction extends WebInspector.Object
+{
+ constructor(breakpoint, typeOrInfo, data)
+ {
+ super();
+
+ console.assert(breakpoint);
+ console.assert(typeOrInfo);
+
+ this._breakpoint = breakpoint;
+
+ if (typeof typeOrInfo === "string") {
+ this._type = typeOrInfo;
+ this._data = data || null;
+ } else if (typeof typeOrInfo === "object") {
+ this._type = typeOrInfo.type;
+ this._data = typeOrInfo.data || null;
+ } else
+ console.error("Unexpected type passed to WebInspector.BreakpointAction");
+
+ console.assert(typeof this._type === "string");
+ this._id = WebInspector.debuggerManager.nextBreakpointActionIdentifier();
+ }
+
+ // Public
+
+ get breakpoint() { return this._breakpoint; }
+ get id() { return this._id; }
+ get type() { return this._type; }
+
+ get data()
+ {
+ return this._data;
+ }
+
+ set data(data)
+ {
+ if (this._data === data)
+ return;
+
+ this._data = data;
+
+ this._breakpoint.breakpointActionDidChange(this);
+ }
+
+ get info()
+ {
+ var obj = {type: this._type, id: this._id};
+ if (this._data)
+ obj.data = this._data;
+ return obj;
+ }
+};
+
+WebInspector.BreakpointAction.Type = {
+ Log: "log",
+ Evaluate: "evaluate",
+ Sound: "sound",
+ Probe: "probe"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js b/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js
new file mode 100644
index 000000000..8cab966b6
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2010 Nikita Vasilyev. All rights reserved.
+ * Copyright (C) 2010 Joseph Pecoraro. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSCompletions = class CSSCompletions
+{
+ constructor(properties, acceptEmptyPrefix)
+ {
+ this._values = [];
+ this._longhands = {};
+ this._shorthands = {};
+
+ // The `properties` parameter can be either a list of objects with 'name' / 'longhand'
+ // properties when initialized from the protocol for CSSCompletions.cssNameCompletions.
+ // Or it may just a list of strings when quickly initialized for other completion purposes.
+ if (properties.length && typeof properties[0] === "string")
+ this._values = this._values.concat(properties);
+ else {
+ for (var property of properties) {
+ var propertyName = property.name;
+ console.assert(propertyName);
+
+ this._values.push(propertyName);
+
+ var longhands = property.longhands;
+ if (longhands) {
+ this._longhands[propertyName] = longhands;
+
+ for (var j = 0; j < longhands.length; ++j) {
+ var longhandName = longhands[j];
+
+ var shorthands = this._shorthands[longhandName];
+ if (!shorthands) {
+ shorthands = [];
+ this._shorthands[longhandName] = shorthands;
+ }
+
+ shorthands.push(propertyName);
+ }
+ }
+ }
+ }
+
+ this._values.sort();
+
+ this._acceptEmptyPrefix = acceptEmptyPrefix;
+ }
+
+ // Static
+
+ static requestCSSCompletions()
+ {
+ if (WebInspector.CSSCompletions.cssNameCompletions)
+ return;
+
+ function propertyNamesCallback(error, names)
+ {
+ if (error)
+ return;
+
+ WebInspector.CSSCompletions.cssNameCompletions = new WebInspector.CSSCompletions(names, false);
+
+ WebInspector.CSSKeywordCompletions.addCustomCompletions(names);
+
+ // CodeMirror is not included by tests so we shouldn't assume it always exists.
+ // If it isn't available we skip MIME type associations.
+ if (!window.CodeMirror)
+ return;
+
+ var propertyNamesForCodeMirror = {};
+ var valueKeywordsForCodeMirror = {"inherit": true, "initial": true, "unset": true, "revert": true, "var": true};
+ var colorKeywordsForCodeMirror = {};
+
+ function nameForCodeMirror(name)
+ {
+ // CodeMirror parses the vendor prefix separate from the property or keyword name,
+ // so we need to strip vendor prefixes from our names. Also strip function parenthesis.
+ return name.replace(/^-[^-]+-/, "").replace(/\(\)$/, "").toLowerCase();
+ }
+
+ function collectPropertyNameForCodeMirror(propertyName)
+ {
+ // Properties can also be value keywords, like when used in a transition.
+ // So we add them to both lists.
+ var codeMirrorPropertyName = nameForCodeMirror(propertyName);
+ propertyNamesForCodeMirror[codeMirrorPropertyName] = true;
+ valueKeywordsForCodeMirror[codeMirrorPropertyName] = true;
+ }
+
+ for (var property of names)
+ collectPropertyNameForCodeMirror(property.name);
+
+ for (var propertyName in WebInspector.CSSKeywordCompletions._propertyKeywordMap) {
+ var keywords = WebInspector.CSSKeywordCompletions._propertyKeywordMap[propertyName];
+ for (var i = 0; i < keywords.length; ++i) {
+ // Skip numbers, like the ones defined for font-weight.
+ if (!isNaN(Number(keywords[i])))
+ continue;
+ valueKeywordsForCodeMirror[nameForCodeMirror(keywords[i])] = true;
+ }
+ }
+
+ WebInspector.CSSKeywordCompletions._colors.forEach(function(colorName) {
+ colorKeywordsForCodeMirror[nameForCodeMirror(colorName)] = true;
+ });
+
+ function updateCodeMirrorCSSMode(mimeType)
+ {
+ var modeSpec = CodeMirror.resolveMode(mimeType);
+
+ console.assert(modeSpec.propertyKeywords);
+ console.assert(modeSpec.valueKeywords);
+ console.assert(modeSpec.colorKeywords);
+
+ modeSpec.propertyKeywords = propertyNamesForCodeMirror;
+ modeSpec.valueKeywords = valueKeywordsForCodeMirror;
+ modeSpec.colorKeywords = colorKeywordsForCodeMirror;
+
+ CodeMirror.defineMIME(mimeType, modeSpec);
+ }
+
+ updateCodeMirrorCSSMode("text/css");
+ updateCodeMirrorCSSMode("text/x-scss");
+ }
+
+ function fontFamilyNamesCallback(error, fontFamilyNames)
+ {
+ if (error)
+ return;
+
+ WebInspector.CSSKeywordCompletions.addPropertyCompletionValues("font-family", fontFamilyNames);
+ WebInspector.CSSKeywordCompletions.addPropertyCompletionValues("font", fontFamilyNames);
+ }
+
+ if (window.CSSAgent) {
+ CSSAgent.getSupportedCSSProperties(propertyNamesCallback);
+
+ // COMPATIBILITY (iOS 9): CSS.getSupportedSystemFontFamilyNames did not exist.
+ if (CSSAgent.getSupportedSystemFontFamilyNames)
+ CSSAgent.getSupportedSystemFontFamilyNames(fontFamilyNamesCallback);
+ }
+ }
+
+ // Public
+
+ get values()
+ {
+ return this._values;
+ }
+
+ startsWith(prefix)
+ {
+ var firstIndex = this._firstIndexOfPrefix(prefix);
+ if (firstIndex === -1)
+ return [];
+
+ var results = [];
+ while (firstIndex < this._values.length && this._values[firstIndex].startsWith(prefix))
+ results.push(this._values[firstIndex++]);
+ return results;
+ }
+
+ _firstIndexOfPrefix(prefix)
+ {
+ if (!this._values.length)
+ return -1;
+ if (!prefix)
+ return this._acceptEmptyPrefix ? 0 : -1;
+
+ var maxIndex = this._values.length - 1;
+ var minIndex = 0;
+ var foundIndex;
+
+ do {
+ var middleIndex = (maxIndex + minIndex) >> 1;
+ if (this._values[middleIndex].startsWith(prefix)) {
+ foundIndex = middleIndex;
+ break;
+ }
+ if (this._values[middleIndex] < prefix)
+ minIndex = middleIndex + 1;
+ else
+ maxIndex = middleIndex - 1;
+ } while (minIndex <= maxIndex);
+
+ if (foundIndex === undefined)
+ return -1;
+
+ while (foundIndex && this._values[foundIndex - 1].startsWith(prefix))
+ foundIndex--;
+
+ return foundIndex;
+ }
+
+ keySet()
+ {
+ if (!this._keySet)
+ this._keySet = this._values.keySet();
+ return this._keySet;
+ }
+
+ next(str, prefix)
+ {
+ return this._closest(str, prefix, 1);
+ }
+
+ previous(str, prefix)
+ {
+ return this._closest(str, prefix, -1);
+ }
+
+ _closest(str, prefix, shift)
+ {
+ if (!str)
+ return "";
+
+ var index = this._values.indexOf(str);
+ if (index === -1)
+ return "";
+
+ if (!prefix) {
+ index = (index + this._values.length + shift) % this._values.length;
+ return this._values[index];
+ }
+
+ var propertiesWithPrefix = this.startsWith(prefix);
+ var j = propertiesWithPrefix.indexOf(str);
+ j = (j + propertiesWithPrefix.length + shift) % propertiesWithPrefix.length;
+ return propertiesWithPrefix[j];
+ }
+
+ isShorthandPropertyName(shorthand)
+ {
+ return shorthand in this._longhands;
+ }
+
+ shorthandsForLonghand(longhand)
+ {
+ return this._shorthands[longhand] || [];
+ }
+
+ isValidPropertyName(name)
+ {
+ return this._values.includes(name);
+ }
+
+ propertyRequiresWebkitPrefix(name)
+ {
+ return this._values.includes("-webkit-" + name) && !this._values.includes(name);
+ }
+
+ getClosestPropertyName(name)
+ {
+ var bestMatches = [{distance: Infinity, name: null}];
+
+ for (var property of this._values) {
+ var distance = name.levenshteinDistance(property);
+
+ if (distance < bestMatches[0].distance)
+ bestMatches = [{distance, name: property}];
+ else if (distance === bestMatches[0].distance)
+ bestMatches.push({distance, name: property});
+ }
+
+ return bestMatches.length < 3 ? bestMatches[0].name : false;
+ }
+};
+
+WebInspector.CSSCompletions.cssNameCompletions = null;
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSKeywordCompletions.js b/Source/WebInspectorUI/UserInterface/Models/CSSKeywordCompletions.js
new file mode 100644
index 000000000..395e4d28b
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSKeywordCompletions.js
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSKeywordCompletions = {};
+
+WebInspector.CSSKeywordCompletions.forProperty = function(propertyName)
+{
+ let acceptedKeywords = ["initial", "unset", "revert", "var()"];
+ let isNotPrefixed = propertyName.charAt(0) !== "-";
+
+ if (propertyName in WebInspector.CSSKeywordCompletions._propertyKeywordMap)
+ acceptedKeywords = acceptedKeywords.concat(WebInspector.CSSKeywordCompletions._propertyKeywordMap[propertyName]);
+ else if (isNotPrefixed && ("-webkit-" + propertyName) in WebInspector.CSSKeywordCompletions._propertyKeywordMap)
+ acceptedKeywords = acceptedKeywords.concat(WebInspector.CSSKeywordCompletions._propertyKeywordMap["-webkit-" + propertyName]);
+
+ if (propertyName in WebInspector.CSSKeywordCompletions._colorAwareProperties)
+ acceptedKeywords = acceptedKeywords.concat(WebInspector.CSSKeywordCompletions._colors);
+ else if (isNotPrefixed && ("-webkit-" + propertyName) in WebInspector.CSSKeywordCompletions._colorAwareProperties)
+ acceptedKeywords = acceptedKeywords.concat(WebInspector.CSSKeywordCompletions._colors);
+ else if (propertyName.endsWith("color"))
+ acceptedKeywords = acceptedKeywords.concat(WebInspector.CSSKeywordCompletions._colors);
+
+ // Only suggest "inherit" on inheritable properties even though it is valid on all properties.
+ if (propertyName in WebInspector.CSSKeywordCompletions.InheritedProperties)
+ acceptedKeywords.push("inherit");
+ else if (isNotPrefixed && ("-webkit-" + propertyName) in WebInspector.CSSKeywordCompletions.InheritedProperties)
+ acceptedKeywords.push("inherit");
+
+ if (acceptedKeywords.includes(WebInspector.CSSKeywordCompletions.AllPropertyNamesPlaceholder) && WebInspector.CSSCompletions.cssNameCompletions) {
+ acceptedKeywords.remove(WebInspector.CSSKeywordCompletions.AllPropertyNamesPlaceholder);
+ acceptedKeywords = acceptedKeywords.concat(WebInspector.CSSCompletions.cssNameCompletions.values);
+ }
+
+ return new WebInspector.CSSCompletions(acceptedKeywords, true);
+};
+
+WebInspector.CSSKeywordCompletions.addCustomCompletions = function(properties)
+{
+ for (var property of properties) {
+ if (property.values)
+ WebInspector.CSSKeywordCompletions.addPropertyCompletionValues(property.name, property.values);
+ }
+};
+
+WebInspector.CSSKeywordCompletions.addPropertyCompletionValues = function(propertyName, newValues)
+{
+ var existingValues = WebInspector.CSSKeywordCompletions._propertyKeywordMap[propertyName];
+ if (!existingValues) {
+ WebInspector.CSSKeywordCompletions._propertyKeywordMap[propertyName] = newValues;
+ return;
+ }
+
+ var union = new Set;
+ for (var value of existingValues)
+ union.add(value);
+ for (var value of newValues)
+ union.add(value);
+
+ WebInspector.CSSKeywordCompletions._propertyKeywordMap[propertyName] = [...union.values()];
+};
+
+WebInspector.CSSKeywordCompletions.AllPropertyNamesPlaceholder = "__all-properties__";
+
+WebInspector.CSSKeywordCompletions.InheritedProperties = [
+ "azimuth", "border-collapse", "border-spacing", "caption-side", "clip-rule", "color", "color-interpolation",
+ "color-interpolation-filters", "color-rendering", "cursor", "direction", "elevation", "empty-cells", "fill",
+ "fill-opacity", "fill-rule", "font", "font-family", "font-size", "font-style", "font-variant", "font-variant-numeric", "font-weight",
+ "glyph-orientation-horizontal", "glyph-orientation-vertical", "hanging-punctuation", "image-rendering", "kerning", "letter-spacing",
+ "line-height", "list-style", "list-style-image", "list-style-position", "list-style-type", "marker", "marker-end",
+ "marker-mid", "marker-start", "orphans", "pitch", "pitch-range", "pointer-events", "quotes", "resize", "richness",
+ "shape-rendering", "speak", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "stroke",
+ "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity",
+ "stroke-width", "tab-size", "text-align", "text-anchor", "text-decoration", "text-indent", "text-rendering",
+ "text-shadow", "text-transform", "visibility", "voice-family", "volume", "white-space", "widows", "word-break",
+ "word-spacing", "word-wrap", "writing-mode", "-webkit-aspect-ratio", "-webkit-border-horizontal-spacing",
+ "-webkit-border-vertical-spacing", "-webkit-box-direction", "-webkit-color-correction", "font-feature-settings",
+ "-webkit-font-kerning", "-webkit-font-smoothing", "-webkit-font-variant-ligatures",
+ "-webkit-hyphenate-character", "-webkit-hyphenate-limit-after", "-webkit-hyphenate-limit-before",
+ "-webkit-hyphenate-limit-lines", "-webkit-hyphens", "-webkit-line-align", "-webkit-line-box-contain",
+ "-webkit-line-break", "-webkit-line-grid", "-webkit-line-snap", "-webkit-locale", "-webkit-nbsp-mode",
+ "-webkit-print-color-adjust", "-webkit-rtl-ordering", "-webkit-text-combine", "-webkit-text-decorations-in-effect",
+ "-webkit-text-emphasis", "-webkit-text-emphasis-color", "-webkit-text-emphasis-position", "-webkit-text-emphasis-style",
+ "-webkit-text-fill-color", "-webkit-text-orientation", "-webkit-text-security", "-webkit-text-size-adjust",
+ "-webkit-text-stroke", "-webkit-text-stroke-color", "-webkit-text-stroke-width", "-webkit-user-modify",
+ "-webkit-user-select", "-webkit-writing-mode", "-webkit-cursor-visibility", "image-orientation", "image-resolution",
+ "overflow-wrap", "-webkit-text-align-last", "-webkit-text-justify", "-webkit-ruby-position", "-webkit-text-decoration-line",
+ "font-synthesis",
+
+ // iOS Properties
+ "-webkit-overflow-scrolling", "-webkit-touch-callout", "-webkit-tap-highlight-color"
+].keySet();
+
+WebInspector.CSSKeywordCompletions._colors = [
+ "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "orange", "purple", "red",
+ "silver", "teal", "white", "yellow", "transparent", "currentcolor", "grey", "aliceblue", "antiquewhite",
+ "aquamarine", "azure", "beige", "bisque", "blanchedalmond", "blueviolet", "brown", "burlywood", "cadetblue",
+ "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan",
+ "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange",
+ "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey",
+ "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick",
+ "floralwhite", "forestgreen", "gainsboro", "ghostwhite", "gold", "goldenrod", "greenyellow", "honeydew", "hotpink",
+ "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
+ "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue", "lightyellow",
+ "limegreen", "linen", "magenta", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
+ "mediumslateblue", "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream",
+ "mistyrose", "moccasin", "navajowhite", "oldlace", "olivedrab", "orangered", "orchid", "palegoldenrod", "palegreen",
+ "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "rebeccapurple", "rosybrown",
+ "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "skyblue", "slateblue",
+ "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", "thistle", "tomato", "turquoise", "violet",
+ "wheat", "whitesmoke", "yellowgreen", "rgb()", "rgba()", "hsl()", "hsla()"
+];
+
+WebInspector.CSSKeywordCompletions._colorAwareProperties = [
+ "background", "background-color", "background-image", "border", "border-color", "border-top", "border-right", "border-bottom",
+ "border-left", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", "box-shadow", "color",
+ "fill", "outline", "outline-color", "stroke", "text-line-through", "text-line-through-color", "text-overline", "text-overline-color",
+ "text-shadow", "text-underline", "text-underline-color", "-webkit-box-shadow", "-webkit-column-rule", "-webkit-column-rule-color",
+ "-webkit-text-emphasis", "-webkit-text-emphasis-color", "-webkit-text-fill-color", "-webkit-text-stroke", "-webkit-text-stroke-color",
+ "-webkit-text-decoration-color",
+
+ // iOS Properties
+ "-webkit-tap-highlight-color"
+].keySet();
+
+WebInspector.CSSKeywordCompletions._propertyKeywordMap = {
+ "table-layout": [
+ "auto", "fixed"
+ ],
+ "visibility": [
+ "hidden", "visible", "collapse"
+ ],
+ "text-underline": [
+ "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+ ],
+ "content": [
+ "list-item", "close-quote", "no-close-quote", "no-open-quote", "open-quote", "attr()", "counter()", "counters()", "url()", "linear-gradient()", "radial-gradient()", "repeating-linear-gradient()", "repeating-radial-gradient()", "-webkit-canvas()", "cross-fade()", "image-set()"
+ ],
+ "list-style-image": [
+ "none", "url()", "linear-gradient()", "radial-gradient()", "repeating-linear-gradient()", "repeating-radial-gradient()", "-webkit-canvas()", "cross-fade()", "image-set()"
+ ],
+ "clear": [
+ "none", "left", "right", "both"
+ ],
+ "fill-rule": [
+ "nonzero", "evenodd"
+ ],
+ "stroke-linecap": [
+ "butt", "round", "square"
+ ],
+ "stroke-linejoin": [
+ "round", "miter", "bevel"
+ ],
+ "baseline-shift": [
+ "baseline", "sub", "super"
+ ],
+ "border-bottom-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "margin-top-collapse": [
+ "collapse", "separate", "discard"
+ ],
+ "-webkit-box-orient": [
+ "horizontal", "vertical", "inline-axis", "block-axis"
+ ],
+ "font-stretch": [
+ "normal", "wider", "narrower", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
+ "semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
+ ],
+ "-webkit-color-correction": [
+ "default", "srgb"
+ ],
+ "border-left-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "-webkit-writing-mode": [
+ "lr", "rl", "tb", "lr-tb", "rl-tb", "tb-rl", "horizontal-tb", "vertical-rl", "vertical-lr", "horizontal-bt"
+ ],
+ "text-line-through-mode": [
+ "continuous", "skip-white-space"
+ ],
+ "text-overline-mode": [
+ "continuous", "skip-white-space"
+ ],
+ "text-underline-mode": [
+ "continuous", "skip-white-space"
+ ],
+ "text-line-through-style": [
+ "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+ ],
+ "text-overline-style": [
+ "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+ ],
+ "text-underline-style": [
+ "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+ ],
+ "border-collapse": [
+ "collapse", "separate"
+ ],
+ "border-top-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "outline-color": [
+ "invert", "-webkit-focus-ring-color"
+ ],
+ "outline-style": [
+ "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double", "auto"
+ ],
+ "cursor": [
+ "none", "copy", "auto", "crosshair", "default", "pointer", "move", "vertical-text", "cell", "context-menu",
+ "alias", "progress", "no-drop", "not-allowed", "zoom-in", "zoom-out", "e-resize", "ne-resize",
+ "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize", "w-resize", "ew-resize", "ns-resize",
+ "nesw-resize", "nwse-resize", "col-resize", "row-resize", "text", "wait", "help", "all-scroll", "-webkit-grab",
+ "-webkit-zoom-in", "-webkit-zoom-out",
+ "-webkit-grabbing", "url()", "image-set()"
+ ],
+ "border-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "size": [
+ "a3", "a4", "a5", "b4", "b5", "landscape", "ledger", "legal", "letter", "portrait"
+ ],
+ "background": [
+ "none", "url()", "linear-gradient()", "radial-gradient()", "repeating-linear-gradient()", "repeating-radial-gradient()", "-webkit-canvas()", "cross-fade()", "image-set()",
+ "repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round",
+ "scroll", "fixed", "local",
+ "auto", "contain", "cover",
+ "top", "right", "left", "bottom", "center",
+ "border-box", "padding-box", "content-box"
+ ],
+ "background-image": [
+ "none", "url()", "linear-gradient()", "radial-gradient()", "repeating-linear-gradient()", "repeating-radial-gradient()", "-webkit-canvas()", "cross-fade()", "image-set()"
+ ],
+ "background-size": [
+ "auto", "contain", "cover"
+ ],
+ "background-attachment": [
+ "scroll", "fixed", "local"
+ ],
+ "background-repeat": [
+ "repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round"
+ ],
+ "background-blend-mode": [
+ "normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"
+ ],
+ "background-position": [
+ "top", "right", "left", "bottom", "center"
+ ],
+ "background-origin": [
+ "border-box", "padding-box", "content-box"
+ ],
+ "background-clip": [
+ "border-box", "padding-box", "content-box"
+ ],
+ "direction": [
+ "ltr", "rtl"
+ ],
+ "enable-background": [
+ "accumulate", "new"
+ ],
+ "float": [
+ "none", "left", "right"
+ ],
+ "hanging-punctuation": [
+ "none", "first", "last", "allow-end", "force-end"
+ ],
+ "overflow-x": [
+ "hidden", "auto", "visible", "overlay", "scroll", "marquee"
+ ],
+ "overflow-y": [
+ "hidden", "auto", "visible", "overlay", "scroll", "marquee", "-webkit-paged-x", "-webkit-paged-y"
+ ],
+ "overflow": [
+ "hidden", "auto", "visible", "overlay", "scroll", "marquee", "-webkit-paged-x", "-webkit-paged-y"
+ ],
+ "margin-bottom-collapse": [
+ "collapse", "separate", "discard"
+ ],
+ "-webkit-box-reflect": [
+ "none", "left", "right", "above", "below"
+ ],
+ "text-rendering": [
+ "auto", "optimizeSpeed", "optimizeLegibility", "geometricPrecision"
+ ],
+ "text-align": [
+ "-webkit-auto", "left", "right", "center", "justify", "-webkit-left", "-webkit-right", "-webkit-center", "-webkit-match-parent", "start", "end"
+ ],
+ "list-style-position": [
+ "outside", "inside"
+ ],
+ "margin-bottom": [
+ "auto"
+ ],
+ "color-interpolation": [
+ "linearrgb"
+ ],
+ "word-wrap": [
+ "normal", "break-word"
+ ],
+ "font-weight": [
+ "normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900"
+ ],
+ "font-synthesis": [
+ "none", "weight", "style"
+ ],
+ "margin-before-collapse": [
+ "collapse", "separate", "discard"
+ ],
+ "text-overline-width": [
+ "normal", "medium", "auto", "thick", "thin", "calc()"
+ ],
+ "text-transform": [
+ "none", "capitalize", "uppercase", "lowercase"
+ ],
+ "border-right-style": [
+ "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+ ],
+ "border-left-style": [
+ "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+ ],
+ "font-style": [
+ "italic", "oblique", "normal"
+ ],
+ "speak": [
+ "none", "normal", "spell-out", "digits", "literal-punctuation", "no-punctuation"
+ ],
+ "text-line-through": [
+ "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave", "continuous", "skip-white-space"
+ ],
+ "color-rendering": [
+ "auto", "optimizeSpeed", "optimizeQuality"
+ ],
+ "list-style-type": [
+ "none", "disc", "circle", "square", "decimal", "decimal-leading-zero", "arabic-indic", "binary", "bengali",
+ "cambodian", "khmer", "devanagari", "gujarati", "gurmukhi", "kannada", "lower-hexadecimal", "lao", "malayalam",
+ "mongolian", "myanmar", "octal", "oriya", "persian", "urdu", "telugu", "tibetan", "thai", "upper-hexadecimal",
+ "lower-roman", "upper-roman", "lower-greek", "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", "afar",
+ "ethiopic-halehame-aa-et", "ethiopic-halehame-aa-er", "amharic", "ethiopic-halehame-am-et", "amharic-abegede",
+ "ethiopic-abegede-am-et", "cjk-earthly-branch", "cjk-heavenly-stem", "ethiopic", "ethiopic-halehame-gez",
+ "ethiopic-abegede", "ethiopic-abegede-gez", "hangul-consonant", "hangul", "lower-norwegian", "oromo",
+ "ethiopic-halehame-om-et", "sidama", "ethiopic-halehame-sid-et", "somali", "ethiopic-halehame-so-et", "tigre",
+ "ethiopic-halehame-tig", "tigrinya-er", "ethiopic-halehame-ti-er", "tigrinya-er-abegede",
+ "ethiopic-abegede-ti-er", "tigrinya-et", "ethiopic-halehame-ti-et", "tigrinya-et-abegede",
+ "ethiopic-abegede-ti-et", "upper-greek", "upper-norwegian", "asterisks", "footnotes", "hebrew", "armenian",
+ "lower-armenian", "upper-armenian", "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha",
+ "katakana-iroha"
+ ],
+ "-webkit-text-combine": [
+ "none", "horizontal"
+ ],
+ "outline": [
+ "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+ ],
+ "font": [
+ "caption", "icon", "menu", "message-box", "small-caption", "-webkit-mini-control", "-webkit-small-control",
+ "-webkit-control", "status-bar", "italic", "oblique", "small-caps", "normal", "bold", "bolder", "lighter",
+ "100", "200", "300", "400", "500", "600", "700", "800", "900", "xx-small", "x-small", "small", "medium",
+ "large", "x-large", "xx-large", "-webkit-xxx-large", "smaller", "larger", "serif", "sans-serif", "cursive",
+ "fantasy", "monospace", "-webkit-body", "-webkit-pictograph", "-apple-system",
+ "-apple-system-headline", "-apple-system-body", "-apple-system-subheadline", "-apple-system-footnote",
+ "-apple-system-caption1", "-apple-system-caption2", "-apple-system-short-headline", "-apple-system-short-body",
+ "-apple-system-short-subheadline", "-apple-system-short-footnote", "-apple-system-short-caption1",
+ "-apple-system-tall-body", "-apple-system-title1", "-apple-system-title2", "-apple-system-title3"
+ ],
+ "dominant-baseline": [
+ "middle", "auto", "central", "text-before-edge", "text-after-edge", "ideographic", "alphabetic", "hanging",
+ "mathematical", "use-script", "no-change", "reset-size"
+ ],
+ "display": [
+ "none", "inline", "block", "list-item", "compact", "inline-block", "table", "inline-table",
+ "table-row-group", "table-header-group", "table-footer-group", "table-row", "table-column-group",
+ "table-column", "table-cell", "table-caption", "-webkit-box", "-webkit-inline-box", "-wap-marquee",
+ "flex", "inline-flex", "grid", "inline-grid"
+ ],
+ "image-rendering": [
+ "auto", "optimizeSpeed", "optimizeQuality", "-webkit-crisp-edges", "-webkit-optimize-contrast", "crisp-edges", "pixelated"
+ ],
+ "alignment-baseline": [
+ "baseline", "middle", "auto", "before-edge", "after-edge", "central", "text-before-edge", "text-after-edge",
+ "ideographic", "alphabetic", "hanging", "mathematical"
+ ],
+ "outline-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "text-line-through-width": [
+ "normal", "medium", "auto", "thick", "thin"
+ ],
+ "box-align": [
+ "baseline", "center", "stretch", "start", "end"
+ ],
+ "box-shadow": [
+ "none"
+ ],
+ "text-shadow": [
+ "none"
+ ],
+ "-webkit-box-shadow": [
+ "none"
+ ],
+ "border-right-width": [
+ "medium", "thick", "thin"
+ ],
+ "border-top-style": [
+ "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+ ],
+ "line-height": [
+ "normal"
+ ],
+ "counter-increment": [
+ "none"
+ ],
+ "counter-reset": [
+ "none"
+ ],
+ "text-overflow": [
+ "clip", "ellipsis"
+ ],
+ "-webkit-box-direction": [
+ "normal", "reverse"
+ ],
+ "margin-after-collapse": [
+ "collapse", "separate", "discard"
+ ],
+ "break-after": [
+ "left", "right", "recto", "verso", "auto", "avoid", "page", "column", "region", "avoid-page", "avoid-column", "avoid-region"
+ ],
+ "break-before": [
+ "left", "right", "recto", "verso", "auto", "avoid", "page", "column", "region", "avoid-page", "avoid-column", "avoid-region"
+ ],
+ "break-inside": [
+ "auto", "avoid", "avoid-page", "avoid-column", "avoid-region"
+ ],
+ "page-break-after": [
+ "left", "right", "auto", "always", "avoid"
+ ],
+ "page-break-before": [
+ "left", "right", "auto", "always", "avoid"
+ ],
+ "page-break-inside": [
+ "auto", "avoid"
+ ],
+ "-webkit-column-break-after": [
+ "left", "right", "auto", "always", "avoid"
+ ],
+ "-webkit-column-break-before": [
+ "left", "right", "auto", "always", "avoid"
+ ],
+ "-webkit-column-break-inside": [
+ "auto", "avoid"
+ ],
+ "-webkit-hyphens": [
+ "none", "auto", "manual"
+ ],
+ "border-image": [
+ "repeat", "stretch", "url()", "linear-gradient()", "radial-gradient()", "repeating-linear-gradient()", "repeating-radial-gradient()", "-webkit-canvas()", "cross-fade()", "image-set()"
+ ],
+ "border-image-repeat": [
+ "repeat", "stretch", "space", "round"
+ ],
+ "-webkit-mask-box-image-repeat": [
+ "repeat", "stretch", "space", "round"
+ ],
+ "position": [
+ "absolute", "fixed", "relative", "static", "-webkit-sticky"
+ ],
+ "font-family": [
+ "serif", "sans-serif", "cursive", "fantasy", "monospace", "-webkit-body", "-webkit-pictograph",
+ "-apple-system", "-apple-system-headline", "-apple-system-body",
+ "-apple-system-subheadline", "-apple-system-footnote", "-apple-system-caption1", "-apple-system-caption2",
+ "-apple-system-short-headline", "-apple-system-short-body", "-apple-system-short-subheadline",
+ "-apple-system-short-footnote", "-apple-system-short-caption1", "-apple-system-tall-body",
+ "-apple-system-title1", "-apple-system-title2", "-apple-system-title3"
+ ],
+ "text-overflow-mode": [
+ "clip", "ellipsis"
+ ],
+ "border-bottom-style": [
+ "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+ ],
+ "unicode-bidi": [
+ "normal", "bidi-override", "embed", "-webkit-plaintext", "-webkit-isolate", "-webkit-isolate-override"
+ ],
+ "clip-rule": [
+ "nonzero", "evenodd"
+ ],
+ "margin-left": [
+ "auto"
+ ],
+ "margin-top": [
+ "auto"
+ ],
+ "zoom": [
+ "normal", "document", "reset"
+ ],
+ "z-index": [
+ "auto"
+ ],
+ "width": [
+ "intrinsic", "min-intrinsic", "-webkit-min-content", "-webkit-max-content", "-webkit-fill-available", "-webkit-fit-content", "calc()"
+ ],
+ "height": [
+ "intrinsic", "min-intrinsic", "calc()"
+ ],
+ "max-width": [
+ "none", "intrinsic", "min-intrinsic", "-webkit-min-content", "-webkit-max-content", "-webkit-fill-available", "-webkit-fit-content", "calc()"
+ ],
+ "min-width": [
+ "intrinsic", "min-intrinsic", "-webkit-min-content", "-webkit-max-content", "-webkit-fill-available", "-webkit-fit-content", "calc()"
+ ],
+ "max-height": [
+ "none", "intrinsic", "min-intrinsic", "calc()"
+ ],
+ "min-height": [
+ "intrinsic", "min-intrinsic", "calc()"
+ ],
+ "-webkit-logical-width": [
+ "intrinsic", "min-intrinsic", "-webkit-min-content", "-webkit-max-content", "-webkit-fill-available", "-webkit-fit-content", "calc()"
+ ],
+ "-webkit-logical-height": [
+ "intrinsic", "min-intrinsic", "calc()"
+ ],
+ "-webkit-max-logical-width": [
+ "none", "intrinsic", "min-intrinsic", "-webkit-min-content", "-webkit-max-content", "-webkit-fill-available", "-webkit-fit-content", "calc()"
+ ],
+ "-webkit-min-logical-width": [
+ "intrinsic", "min-intrinsic", "-webkit-min-content", "-webkit-max-content", "-webkit-fill-available", "-webkit-fit-content", "calc()"
+ ],
+ "-webkit-max-logical-height": [
+ "none", "intrinsic", "min-intrinsic", "calc()"
+ ],
+ "-webkit-min-logical-height": [
+ "intrinsic", "min-intrinsic", "calc()"
+ ],
+ "empty-cells": [
+ "hide", "show"
+ ],
+ "pointer-events": [
+ "none", "all", "auto", "visible", "visiblepainted", "visiblefill", "visiblestroke", "painted", "fill", "stroke"
+ ],
+ "letter-spacing": [
+ "normal", "calc()"
+ ],
+ "word-spacing": [
+ "normal", "calc()"
+ ],
+ "-webkit-font-kerning": [
+ "auto", "normal", "none"
+ ],
+ "-webkit-font-smoothing": [
+ "none", "auto", "antialiased", "subpixel-antialiased"
+ ],
+ "border": [
+ "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+ ],
+ "font-size": [
+ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "-webkit-xxx-large", "smaller", "larger"
+ ],
+ "font-variant": [
+ "small-caps", "normal"
+ ],
+ "font-variant-numeric": [
+ "normal", "ordinal", "slashed-zero", "lining-nums", "oldstyle-nums", "proportional-nums", "tabular-nums",
+ "diagonal-fractions", "stacked-fractions"
+ ],
+ "vertical-align": [
+ "baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom", "-webkit-baseline-middle"
+ ],
+ "white-space": [
+ "normal", "nowrap", "pre", "pre-line", "pre-wrap"
+ ],
+ "word-break": [
+ "normal", "break-all", "break-word"
+ ],
+ "text-underline-width": [
+ "normal", "medium", "auto", "thick", "thin", "calc()"
+ ],
+ "text-indent": [
+ "-webkit-each-line", "-webkit-hanging"
+ ],
+ "-webkit-box-lines": [
+ "single", "multiple"
+ ],
+ "clip": [
+ "auto", "rect()"
+ ],
+ "clip-path": [
+ "none", "url()", "circle()", "ellipse()", "inset()", "polygon()", "margin-box", "border-box", "padding-box", "content-box"
+ ],
+ "shape-outside": [
+ "none", "url()", "circle()", "ellipse()", "inset()", "polygon()", "margin-box", "border-box", "padding-box", "content-box"
+ ],
+ "orphans": [
+ "auto"
+ ],
+ "widows": [
+ "auto"
+ ],
+ "margin": [
+ "auto"
+ ],
+ "page": [
+ "auto"
+ ],
+ "perspective": [
+ "none"
+ ],
+ "perspective-origin": [
+ "none", "left", "right", "bottom", "top", "center"
+ ],
+ "-webkit-marquee-increment": [
+ "small", "large", "medium"
+ ],
+ "-webkit-marquee-direction": [
+ "left", "right", "auto", "reverse", "forwards", "backwards", "ahead", "up", "down"
+ ],
+ "-webkit-marquee-style": [
+ "none", "scroll", "slide", "alternate"
+ ],
+ "-webkit-marquee-repetition": [
+ "infinite"
+ ],
+ "-webkit-marquee-speed": [
+ "normal", "slow", "fast"
+ ],
+ "margin-right": [
+ "auto"
+ ],
+ "marquee-speed": [
+ "normal", "slow", "fast"
+ ],
+ "-webkit-text-emphasis": [
+ "circle", "filled", "open", "dot", "double-circle", "triangle", "sesame"
+ ],
+ "-webkit-text-emphasis-style": [
+ "circle", "filled", "open", "dot", "double-circle", "triangle", "sesame"
+ ],
+ "-webkit-text-emphasis-position": [
+ "over", "under", "left", "right"
+ ],
+ "transform": [
+ "none",
+ "scale()", "scaleX()", "scaleY()", "scale3d()", "rotate()", "rotateX()", "rotateY()", "rotateZ()", "rotate3d()", "skew()", "skewX()", "skewY()",
+ "translate()", "translateX()", "translateY()", "translateZ()", "translate3d()", "matrix()", "matrix3d()", "perspective()"
+ ],
+ "transform-style": [
+ "flat", "preserve-3d"
+ ],
+ "-webkit-cursor-visibility": [
+ "auto", "auto-hide"
+ ],
+ "text-decoration": [
+ "none", "underline", "overline", "line-through", "blink"
+ ],
+ "-webkit-text-decorations-in-effect": [
+ "none", "underline", "overline", "line-through", "blink"
+ ],
+ "-webkit-text-decoration-line": [
+ "none", "underline", "overline", "line-through", "blink"
+ ],
+ "-webkit-text-decoration-style": [
+ "solid", "double", "dotted", "dashed", "wavy"
+ ],
+ "-webkit-text-decoration-skip": [
+ "auto", "none", "objects", "ink"
+ ],
+ "-webkit-text-underline-position": [
+ "auto", "alphabetic", "under"
+ ],
+ "image-resolution": [
+ "from-image", "snap"
+ ],
+ "-webkit-blend-mode": [
+ "normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "plus-darker", "plus-lighter", "hue", "saturation", "color", "luminosity",
+ ],
+ "mix-blend-mode": [
+ "normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "plus-darker", "plus-lighter", "hue", "saturation", "color", "luminosity",
+ ],
+ "mix": [
+ "auto",
+ "normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "plus-darker", "plus-lighter", "hue", "saturation", "color", "luminosity",
+ "clear", "copy", "destination", "source-over", "destination-over", "source-in", "destination-in", "source-out", "destination-out", "source-atop", "destination-atop", "xor"
+ ],
+ "geometry": [
+ "detached", "attached", "grid()"
+ ],
+ "overflow-wrap": [
+ "normal", "break-word"
+ ],
+ "transition": [
+ "none", "ease", "linear", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "steps()", "cubic-bezier()", "spring()", "all", WebInspector.CSSKeywordCompletions.AllPropertyNamesPlaceholder
+ ],
+ "transition-timing-function": [
+ "ease", "linear", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "steps()", "cubic-bezier()", "spring()"
+ ],
+ "transition-property": [
+ "all", "none", WebInspector.CSSKeywordCompletions.AllPropertyNamesPlaceholder
+ ],
+ "-webkit-column-progression": [
+ "normal", "reverse"
+ ],
+ "-webkit-box-decoration-break": [
+ "slice", "clone"
+ ],
+ "align-content": [
+ "auto",
+ "baseline", "last-baseline",
+ "space-between", "space-around", "space-evenly", "stretch",
+ "center", "start", "end", "flex-start", "flex-end", "left", "right",
+ "true", "safe"
+ ],
+ "justify-content": [
+ "auto",
+ "baseline", "last-baseline", "space-between", "space-around", "space-evenly", "stretch",
+ "center", "start", "end", "flex-start", "flex-end", "left", "right",
+ "true", "safe"
+ ],
+ "align-items": [
+ "auto", "stretch",
+ "baseline", "last-baseline",
+ "center", "start", "end", "self-start", "self-end", "flex-start", "flex-end", "left", "right",
+ "true", "safe"
+ ],
+ "align-self": [
+ "auto", "stretch",
+ "baseline", "last-baseline",
+ "center", "start", "end", "self-start", "self-end", "flex-start", "flex-end", "left", "right",
+ "true", "safe"
+ ],
+ "justify-items": [
+ "auto", "stretch",
+ "baseline", "last-baseline",
+ "center", "start", "end", "self-start", "self-end", "flex-start", "flex-end", "left", "right",
+ "true", "safe"
+ ],
+ "justify-self": [
+ "auto", "stretch",
+ "baseline", "last-baseline",
+ "center", "start", "end", "self-start", "self-end", "flex-start", "flex-end", "left", "right",
+ "true", "safe"
+ ],
+ "flex-direction": [
+ "row", "row-reverse", "column", "column-reverse"
+ ],
+ "flex-wrap": [
+ "nowrap", "wrap", "wrap-reverse"
+ ],
+ "flex-flow": [
+ "row", "row-reverse", "column", "column-reverse",
+ "nowrap", "wrap", "wrap-reverse"
+ ],
+ "flex": [
+ "none"
+ ],
+ "flex-basis": [
+ "auto"
+ ],
+ "grid": [
+ "none"
+ ],
+ "grid-area": [
+ "auto"
+ ],
+ "grid-auto-columns": [
+ "auto", "-webkit-max-content", "-webkit-min-content", "minmax()",
+ ],
+ "grid-auto-flow": [
+ "row", "column", "dense"
+ ],
+ "grid-auto-rows": [
+ "auto", "-webkit-max-content", "-webkit-min-content", "minmax()",
+ ],
+ "grid-column": [
+ "auto"
+ ],
+ "grid-column-start": [
+ "auto"
+ ],
+ "grid-column-end": [
+ "auto"
+ ],
+ "grid-row": [
+ "auto"
+ ],
+ "grid-row-start": [
+ "auto"
+ ],
+ "grid-row-end": [
+ "auto"
+ ],
+ "grid-template": [
+ "none"
+ ],
+ "grid-template-areas": [
+ "none"
+ ],
+ "grid-template-columns": [
+ "none", "auto", "-webkit-max-content", "-webkit-min-content", "minmax()", "repeat()"
+ ],
+ "grid-template-rows": [
+ "none", "auto", "-webkit-max-content", "-webkit-min-content", "minmax()", "repeat()"
+ ],
+ "-webkit-ruby-position": [
+ "after", "before", "inter-character"
+ ],
+ "-webkit-text-align-last": [
+ "auto", "start", "end", "left", "right", "center", "justify"
+ ],
+ "-webkit-text-justify": [
+ "auto", "none", "inter-word", "inter-ideograph", "inter-cluster", "distribute", "kashida"
+ ],
+ "max-zoom": [
+ "auto"
+ ],
+ "min-zoom": [
+ "auto"
+ ],
+ "orientation": [
+ "auto", "portait", "landscape"
+ ],
+ "scroll-snap-align": [
+ "none", "start", "center", "end"
+ ],
+ "scroll-snap-type": [
+ "none", "mandatory", "proximity", "x", "y", "inline", "block", "both"
+ ],
+ "user-zoom": [
+ "zoom", "fixed"
+ ],
+ "-webkit-app-region": [
+ "drag", "no-drag"
+ ],
+ "-webkit-line-break": [
+ "auto", "loose", "normal", "strict", "after-white-space"
+ ],
+ "-webkit-background-composite": [
+ "clear", "copy", "source-over", "source-in", "source-out", "source-atop", "destination-over", "destination-in", "destination-out", "destination-atop", "xor", "plus-darker", "plus-lighter"
+ ],
+ "-webkit-mask-composite": [
+ "clear", "copy", "source-over", "source-in", "source-out", "source-atop", "destination-over", "destination-in", "destination-out", "destination-atop", "xor", "plus-darker", "plus-lighter"
+ ],
+ "-webkit-animation-direction": [
+ "normal", "alternate", "reverse", "alternate-reverse"
+ ],
+ "-webkit-animation-fill-mode": [
+ "none", "forwards", "backwards", "both"
+ ],
+ "-webkit-animation-iteration-count": [
+ "infinite"
+ ],
+ "-webkit-animation-play-state": [
+ "paused", "running"
+ ],
+ "-webkit-animation-timing-function": [
+ "ease", "linear", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "steps()", "cubic-bezier()", "spring()"
+ ],
+ "-webkit-column-span": [
+ "all", "none", "calc()"
+ ],
+ "-webkit-region-break-after": [
+ "auto", "always", "avoid", "left", "right"
+ ],
+ "-webkit-region-break-before": [
+ "auto", "always", "avoid", "left", "right"
+ ],
+ "-webkit-region-break-inside": [
+ "auto", "avoid"
+ ],
+ "-webkit-region-overflow": [
+ "auto", "break"
+ ],
+ "-webkit-backface-visibility": [
+ "visible", "hidden"
+ ],
+ "resize": [
+ "none", "both", "horizontal", "vertical", "auto"
+ ],
+ "caption-side": [
+ "top", "bottom", "left", "right"
+ ],
+ "box-sizing": [
+ "border-box", "content-box"
+ ],
+ "-webkit-alt": [
+ "attr()"
+ ],
+ "-webkit-border-fit": [
+ "border", "lines"
+ ],
+ "-webkit-line-align": [
+ "none", "edges"
+ ],
+ "-webkit-line-snap": [
+ "none", "baseline", "contain"
+ ],
+ "-webkit-nbsp-mode": [
+ "normal", "space"
+ ],
+ "-webkit-print-color-adjust": [
+ "exact", "economy"
+ ],
+ "-webkit-rtl-ordering": [
+ "logical", "visual"
+ ],
+ "-webkit-text-security": [
+ "disc", "circle", "square", "none"
+ ],
+ "-webkit-user-drag": [
+ "auto", "none", "element"
+ ],
+ "-webkit-user-modify": [
+ "read-only", "read-write", "read-write-plaintext-only"
+ ],
+ "-webkit-user-select": [
+ "auto", "none", "text", "all"
+ ],
+ "-webkit-text-stroke-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "-webkit-border-start-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "-webkit-border-end-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "-webkit-border-before-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "-webkit-border-after-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "-webkit-column-rule-width": [
+ "medium", "thick", "thin", "calc()"
+ ],
+ "-webkit-aspect-ratio": [
+ "auto", "from-dimensions", "from-intrinsic", "/"
+ ],
+ "filter": [
+ "none", "grayscale()", "sepia()", "saturate()", "hue-rotate()", "invert()", "opacity()", "brightness()", "contrast()", "blur()", "drop-shadow()", "custom()"
+ ],
+ "-webkit-backdrop-filter": [
+ "none", "grayscale()", "sepia()", "saturate()", "hue-rotate()", "invert()", "opacity()", "brightness()", "contrast()", "blur()", "drop-shadow()", "custom()"
+ ],
+ "-webkit-column-count": [
+ "auto", "calc()"
+ ],
+ "-webkit-column-gap": [
+ "normal", "calc()"
+ ],
+ "-webkit-column-axis": [
+ "horizontal", "vertical", "auto"
+ ],
+ "-webkit-column-width": [
+ "auto", "calc()"
+ ],
+ "-webkit-column-fill": [
+ "auto", "balance"
+ ],
+ "-webkit-hyphenate-character": [
+ "none"
+ ],
+ "-webkit-hyphenate-limit-after": [
+ "auto"
+ ],
+ "-webkit-hyphenate-limit-before": [
+ "auto"
+ ],
+ "-webkit-hyphenate-limit-lines": [
+ "no-limit"
+ ],
+ "-webkit-line-grid": [
+ "none"
+ ],
+ "-webkit-locale": [
+ "auto"
+ ],
+ "-webkit-text-orientation": [
+ "sideways", "sideways-right", "vertical-right", "upright"
+ ],
+ "-webkit-line-box-contain": [
+ "block", "inline", "font", "glyphs", "replaced", "inline-box", "none"
+ ],
+ "font-feature-settings": [
+ "normal"
+ ],
+ "-webkit-font-variant-ligatures": [
+ "normal", "common-ligatures", "no-common-ligatures", "discretionary-ligatures", "no-discretionary-ligatures", "historical-ligatures", "no-historical-ligatures"
+ ],
+ /*
+ "-webkit-appearance": [
+ "none", "checkbox", "radio", "push-button", "square-button", "button", "button-bevel", "default-button", "inner-spin-button", "listbox", "listitem", "media-enter-fullscreen-button", "media-exit-fullscreen-button", "media-fullscreen-volume-slider", "media-fullscreen-volume-slider-thumb", "media-mute-button", "media-play-button", "media-overlay-play-button", "media-seek-back-button", "media-seek-forward-button", "media-rewind-button", "media-return-to-realtime-button", "media-toggle-closed-captions-button", "media-slider", "media-sliderthumb", "media-volume-slider-container", "media-volume-slider", "media-volume-sliderthumb", "media-volume-slider-mute-button", "media-controls-background", "media-controls-fullscreen-background", "media-current-time-display", "media-time-remaining-display", "menulist", "menulist-button", "menulist-text", "menulist-textfield", "meter", "progress-bar", "progress-bar-value", "slider-horizontal", "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "caret", "searchfield", "searchfield-decoration", "searchfield-results-decoration", "searchfield-results-button", "searchfield-cancel-button", "snapshotted-plugin-overlay", "textfield", "relevancy-level-indicator", "continuous-capacity-level-indicator", "discrete-capacity-level-indicator", "rating-level-indicator", "textarea", "attachment", "caps-lock-indicator"
+ ],
+ */
+ "-webkit-animation-trigger": [
+ "auto", "container-scroll()"
+ ],
+
+ // iOS Properties
+ "-webkit-text-size-adjust": [
+ "none", "auto"
+ ],
+ "-webkit-touch-callout": [
+ "default", "none"
+ ],
+ "-webkit-overflow-scrolling": [
+ "auto", "touch"
+ ]
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSMedia.js b/Source/WebInspectorUI/UserInterface/Models/CSSMedia.js
new file mode 100644
index 000000000..2128e8a63
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSMedia.js
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSMedia = class CSSMedia extends WebInspector.Object
+{
+ constructor(type, text, sourceCodeLocation)
+ {
+ super();
+
+ console.assert(!sourceCodeLocation || sourceCodeLocation instanceof WebInspector.SourceCodeLocation);
+
+ this._type = type || null;
+ this._text = text || "";
+ this._sourceCodeLocation = sourceCodeLocation || null;
+ }
+
+ // Public
+
+ get type() { return this._type; }
+ get text() { return this._text; }
+ get sourceCodeLocation() { return this._sourceCodeLocation; }
+};
+
+WebInspector.CSSMedia.Type = {
+ MediaRule: "css-media-type-media-rule",
+ ImportRule: "css-media-type-import-rule",
+ LinkedStyleSheet: "css-media-type-linked-stylesheet",
+ InlineStyleSheet: "css-media-type-inline-stylesheet"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js b/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js
new file mode 100644
index 000000000..860f1efff
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSProperty = class CSSProperty extends WebInspector.Object
+{
+ constructor(index, text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange)
+ {
+ super();
+
+ this._ownerStyle = null;
+ this._index = index;
+
+ this.update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, true);
+ }
+
+ // Static
+
+ static isInheritedPropertyName(name)
+ {
+ console.assert(typeof name === "string");
+ if (name in WebInspector.CSSKeywordCompletions.InheritedProperties)
+ return true;
+ // Check if the name is a CSS variable.
+ return name.startsWith("--");
+ }
+
+ // Public
+
+ get ownerStyle()
+ {
+ return this._ownerStyle;
+ }
+
+ set ownerStyle(ownerStyle)
+ {
+ this._ownerStyle = ownerStyle || null;
+ }
+
+ get index()
+ {
+ return this._index;
+ }
+
+ set index(index)
+ {
+ this._index = index;
+ }
+
+ update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, dontFireEvents)
+ {
+ text = text || "";
+ name = name || "";
+ value = value || "";
+ priority = priority || "";
+ enabled = enabled || false;
+ overridden = overridden || false;
+ implicit = implicit || false;
+ anonymous = anonymous || false;
+ valid = valid || false;
+
+ var changed = false;
+
+ if (!dontFireEvents) {
+ changed = this._name !== name || this._value !== value || this._priority !== priority ||
+ this._enabled !== enabled || this._implicit !== implicit || this._anonymous !== anonymous || this._valid !== valid;
+ }
+
+ // Use the setter for overridden if we want to fire events since the
+ // OverriddenStatusChanged event coalesces changes before it fires.
+ if (!dontFireEvents)
+ this.overridden = overridden;
+ else
+ this._overridden = overridden;
+
+ this._text = text;
+ this._name = name;
+ this._value = value;
+ this._priority = priority;
+ this._enabled = enabled;
+ this._implicit = implicit;
+ this._anonymous = anonymous;
+ this._inherited = WebInspector.CSSProperty.isInheritedPropertyName(name);
+ this._valid = valid;
+ this._variable = name.startsWith("--");
+ this._styleSheetTextRange = styleSheetTextRange || null;
+
+ this._relatedShorthandProperty = null;
+ this._relatedLonghandProperties = [];
+
+ // Clear computed properties.
+ delete this._styleDeclarationTextRange;
+ delete this._canonicalName;
+ delete this._hasOtherVendorNameOrKeyword;
+
+ if (changed)
+ this.dispatchEventToListeners(WebInspector.CSSProperty.Event.Changed);
+ }
+
+ get synthesizedText()
+ {
+ var name = this.name;
+ if (!name)
+ return "";
+
+ var priority = this.priority;
+ return name + ": " + this.value.trim() + (priority ? " !" + priority : "") + ";";
+ }
+
+ get text()
+ {
+ return this._text || this.synthesizedText;
+ }
+
+ get name() { return this._name; }
+
+ get canonicalName()
+ {
+ if (this._canonicalName)
+ return this._canonicalName;
+
+ this._canonicalName = WebInspector.cssStyleManager.canonicalNameForPropertyName(this.name);
+
+ return this._canonicalName;
+ }
+
+ get value() { return this._value; }
+
+ get important()
+ {
+ return this.priority === "important";
+ }
+
+ get priority() { return this._priority; }
+
+ get enabled()
+ {
+ return this._enabled && this._ownerStyle && (!isNaN(this._index) || this._ownerStyle.type === WebInspector.CSSStyleDeclaration.Type.Computed);
+ }
+
+ get overridden() { return this._overridden; }
+ set overridden(overridden)
+ {
+ overridden = overridden || false;
+
+ if (this._overridden === overridden)
+ return;
+
+ var previousOverridden = this._overridden;
+
+ this._overridden = overridden;
+
+ if (this._overriddenStatusChangedTimeout)
+ return;
+
+ function delayed()
+ {
+ delete this._overriddenStatusChangedTimeout;
+
+ if (this._overridden === previousOverridden)
+ return;
+
+ this.dispatchEventToListeners(WebInspector.CSSProperty.Event.OverriddenStatusChanged);
+ }
+
+ this._overriddenStatusChangedTimeout = setTimeout(delayed.bind(this), 0);
+ }
+
+ get implicit() { return this._implicit; }
+ set implicit(implicit) { this._implicit = implicit; }
+
+ get anonymous() { return this._anonymous; }
+ get inherited() { return this._inherited; }
+ get valid() { return this._valid; }
+ get variable() { return this._variable; }
+ get styleSheetTextRange() { return this._styleSheetTextRange; }
+
+ get styleDeclarationTextRange()
+ {
+ if ("_styleDeclarationTextRange" in this)
+ return this._styleDeclarationTextRange;
+
+ if (!this._ownerStyle || !this._styleSheetTextRange)
+ return null;
+
+ var styleTextRange = this._ownerStyle.styleSheetTextRange;
+ if (!styleTextRange)
+ return null;
+
+ var startLine = this._styleSheetTextRange.startLine - styleTextRange.startLine;
+ var endLine = this._styleSheetTextRange.endLine - styleTextRange.startLine;
+
+ var startColumn = this._styleSheetTextRange.startColumn;
+ if (!startLine)
+ startColumn -= styleTextRange.startColumn;
+
+ var endColumn = this._styleSheetTextRange.endColumn;
+ if (!endLine)
+ endColumn -= styleTextRange.startColumn;
+
+ this._styleDeclarationTextRange = new WebInspector.TextRange(startLine, startColumn, endLine, endColumn);
+
+ return this._styleDeclarationTextRange;
+ }
+
+ get relatedShorthandProperty() { return this._relatedShorthandProperty; }
+ set relatedShorthandProperty(property)
+ {
+ this._relatedShorthandProperty = property || null;
+ }
+
+ get relatedLonghandProperties() { return this._relatedLonghandProperties; }
+
+ addRelatedLonghandProperty(property)
+ {
+ this._relatedLonghandProperties.push(property);
+ }
+
+ clearRelatedLonghandProperties(property)
+ {
+ this._relatedLonghandProperties = [];
+ }
+
+ hasOtherVendorNameOrKeyword()
+ {
+ if ("_hasOtherVendorNameOrKeyword" in this)
+ return this._hasOtherVendorNameOrKeyword;
+
+ this._hasOtherVendorNameOrKeyword = WebInspector.cssStyleManager.propertyNameHasOtherVendorPrefix(this.name) || WebInspector.cssStyleManager.propertyValueHasOtherVendorKeyword(this.value);
+
+ return this._hasOtherVendorNameOrKeyword;
+ }
+};
+
+WebInspector.CSSProperty.Event = {
+ Changed: "css-property-changed",
+ OverriddenStatusChanged: "css-property-overridden-status-changed"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSRule.js b/Source/WebInspectorUI/UserInterface/Models/CSSRule.js
new file mode 100644
index 000000000..b20a96598
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSRule.js
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSRule = class CSSRule extends WebInspector.Object
+{
+ constructor(nodeStyles, ownerStyleSheet, id, type, sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, mediaList)
+ {
+ super();
+
+ console.assert(nodeStyles);
+ this._nodeStyles = nodeStyles;
+
+ this._ownerStyleSheet = ownerStyleSheet || null;
+ this._id = id || null;
+ this._type = type || null;
+
+ this.update(sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, mediaList, true);
+ }
+
+ // Public
+
+ get id()
+ {
+ return this._id;
+ }
+
+ get ownerStyleSheet()
+ {
+ return this._ownerStyleSheet;
+ }
+
+ get editable()
+ {
+ return !!this._id && (this._type === WebInspector.CSSStyleSheet.Type.Author || this._type === WebInspector.CSSStyleSheet.Type.Inspector);
+ }
+
+ update(sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, mediaList, dontFireEvents)
+ {
+ sourceCodeLocation = sourceCodeLocation || null;
+ selectorText = selectorText || "";
+ selectors = selectors || [];
+ matchedSelectorIndices = matchedSelectorIndices || [];
+ style = style || null;
+ mediaList = mediaList || [];
+
+ var changed = false;
+ if (!dontFireEvents) {
+ changed = this._selectorText !== selectorText || !Array.shallowEqual(this._selectors, selectors) ||
+ !Array.shallowEqual(this._matchedSelectorIndices, matchedSelectorIndices) || this._style !== style ||
+ !!this._sourceCodeLocation !== !!sourceCodeLocation || this._mediaList.length !== mediaList.length;
+ // FIXME: Look for differences in the media list arrays.
+ }
+
+ if (this._style)
+ this._style.ownerRule = null;
+
+ this._sourceCodeLocation = sourceCodeLocation;
+ this._selectorText = selectorText;
+ this._selectors = selectors;
+ this._matchedSelectorIndices = matchedSelectorIndices;
+ this._mostSpecificSelector = null;
+ this._style = style;
+ this._mediaList = mediaList;
+
+ this._matchedSelectors = null;
+ this._matchedSelectorText = null;
+
+ if (this._style)
+ this._style.ownerRule = this;
+
+ if (changed)
+ this.dispatchEventToListeners(WebInspector.CSSRule.Event.Changed);
+ }
+
+ get type()
+ {
+ return this._type;
+ }
+
+ get sourceCodeLocation()
+ {
+ return this._sourceCodeLocation;
+ }
+
+ get selectorText()
+ {
+ return this._selectorText;
+ }
+
+ set selectorText(selectorText)
+ {
+ console.assert(this.editable);
+ if (!this.editable)
+ return;
+
+ if (this._selectorText === selectorText) {
+ this._selectorResolved(true);
+ return;
+ }
+
+ this._nodeStyles.changeRuleSelector(this, selectorText).then(this._selectorResolved.bind(this), this._selectorRejected.bind(this));
+ }
+
+ get selectors()
+ {
+ return this._selectors;
+ }
+
+ get matchedSelectorIndices()
+ {
+ return this._matchedSelectorIndices;
+ }
+
+ get matchedSelectors()
+ {
+ if (this._matchedSelectors)
+ return this._matchedSelectors;
+
+ this._matchedSelectors = this._selectors.filter(function(element, index) {
+ return this._matchedSelectorIndices.includes(index);
+ }, this);
+
+ return this._matchedSelectors;
+ }
+
+ get matchedSelectorText()
+ {
+ if ("_matchedSelectorText" in this)
+ return this._matchedSelectorText;
+
+ this._matchedSelectorText = this.matchedSelectors.map(function(x) { return x.text; }).join(", ");
+
+ return this._matchedSelectorText;
+ }
+
+ hasMatchedPseudoElementSelector()
+ {
+ if (this.nodeStyles && this.nodeStyles.node && this.nodeStyles.node.isPseudoElement())
+ return true;
+
+ return this.matchedSelectors.some((selector) => selector.isPseudoElementSelector());
+ }
+
+ get style()
+ {
+ return this._style;
+ }
+
+ get mediaList()
+ {
+ return this._mediaList;
+ }
+
+ get mediaText()
+ {
+ if (!this._mediaList.length)
+ return "";
+
+ let mediaText = "";
+ for (let media of this._mediaList)
+ mediaText += media.text;
+
+ return mediaText;
+ }
+
+ isEqualTo(rule)
+ {
+ if (!rule)
+ return false;
+
+ return Object.shallowEqual(this._id, rule.id);
+ }
+
+ get mostSpecificSelector()
+ {
+ if (!this._mostSpecificSelector)
+ this._mostSpecificSelector = this._determineMostSpecificSelector();
+
+ return this._mostSpecificSelector;
+ }
+
+ selectorIsGreater(otherSelector)
+ {
+ var mostSpecificSelector = this.mostSpecificSelector;
+
+ if (!mostSpecificSelector)
+ return false;
+
+ return mostSpecificSelector.isGreaterThan(otherSelector);
+ }
+
+ // Protected
+
+ get nodeStyles()
+ {
+ return this._nodeStyles;
+ }
+
+ // Private
+
+ _determineMostSpecificSelector()
+ {
+ if (!this._selectors || !this._selectors.length)
+ return null;
+
+ var selectors = this.matchedSelectors;
+
+ if (!selectors.length)
+ selectors = this._selectors;
+
+ var specificSelector = selectors[0];
+
+ for (var selector of selectors) {
+ if (selector.isGreaterThan(specificSelector))
+ specificSelector = selector;
+ }
+
+ return specificSelector;
+ }
+
+ _selectorRejected(error)
+ {
+ this.dispatchEventToListeners(WebInspector.CSSRule.Event.SelectorChanged, {valid: !error});
+ }
+
+ _selectorResolved(rulePayload)
+ {
+ this.dispatchEventToListeners(WebInspector.CSSRule.Event.SelectorChanged, {valid: !!rulePayload});
+ }
+};
+
+WebInspector.CSSRule.Event = {
+ Changed: "css-rule-changed",
+ SelectorChanged: "css-rule-invalid-selector"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSSelector.js b/Source/WebInspectorUI/UserInterface/Models/CSSSelector.js
new file mode 100644
index 000000000..892e446c3
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSSelector.js
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSSelector = class CSSSelector extends WebInspector.Object
+{
+ constructor(text, specificity, dynamic)
+ {
+ super();
+
+ console.assert(text);
+
+ this._text = text;
+ this._specificity = specificity || null;
+ this._dynamic = dynamic || false;
+ }
+
+ // Public
+
+ get text() { return this._text; }
+ get specificity() { return this._specificity; }
+ get dynamic() { return this._dynamic; }
+
+ isGreaterThan(selector)
+ {
+ if (!selector || !selector.specificity)
+ return true;
+
+ for (var i = 0; i < this._specificity.length; ++i) {
+ if (this._specificity[i] === selector.specificity[i])
+ continue;
+
+ return this._specificity[i] > selector.specificity[i];
+ }
+
+ return false;
+ }
+
+ isPseudoElementSelector()
+ {
+ return WebInspector.CSSStyleManager.PseudoElementNames.some((name) => this._text.includes(`:${name}`));
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js b/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js
new file mode 100644
index 000000000..8a7ba5f8f
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSStyleDeclaration = class CSSStyleDeclaration extends WebInspector.Object
+{
+ constructor(nodeStyles, ownerStyleSheet, id, type, node, inherited, text, properties, styleSheetTextRange)
+ {
+ super();
+
+ console.assert(nodeStyles);
+ this._nodeStyles = nodeStyles;
+
+ this._ownerRule = null;
+
+ this._ownerStyleSheet = ownerStyleSheet || null;
+ this._id = id || null;
+ this._type = type || null;
+ this._node = node || null;
+ this._inherited = inherited || false;
+
+ this._pendingProperties = [];
+ this._propertyNameMap = {};
+
+ this._initialText = text;
+ this._hasModifiedInitialText = false;
+
+ this.update(text, properties, styleSheetTextRange, true);
+ }
+
+ // Public
+
+ get id()
+ {
+ return this._id;
+ }
+
+ get ownerStyleSheet()
+ {
+ return this._ownerStyleSheet;
+ }
+
+ get type()
+ {
+ return this._type;
+ }
+
+ get inherited()
+ {
+ return this._inherited;
+ }
+
+ get node()
+ {
+ return this._node;
+ }
+
+ get editable()
+ {
+ if (!this._id)
+ return false;
+
+ if (this._type === WebInspector.CSSStyleDeclaration.Type.Rule)
+ return this._ownerRule && this._ownerRule.editable;
+
+ if (this._type === WebInspector.CSSStyleDeclaration.Type.Inline)
+ return !this._node.isInUserAgentShadowTree();
+
+ return false;
+ }
+
+ update(text, properties, styleSheetTextRange, dontFireEvents)
+ {
+ text = text || "";
+ properties = properties || [];
+
+ var oldProperties = this._properties || [];
+ var oldText = this._text;
+
+ this._text = text;
+ this._properties = properties;
+ this._styleSheetTextRange = styleSheetTextRange;
+ this._propertyNameMap = {};
+
+ delete this._visibleProperties;
+
+ var editable = this.editable;
+
+ for (var i = 0; i < this._properties.length; ++i) {
+ var property = this._properties[i];
+ property.ownerStyle = this;
+
+ // Store the property in a map if we arn't editable. This
+ // allows for quick lookup for computed style. Editable
+ // styles don't use the map since they need to account for
+ // overridden properties.
+ if (!editable)
+ this._propertyNameMap[property.name] = property;
+ else {
+ // Remove from pendingProperties (if it was pending).
+ this._pendingProperties.remove(property);
+ }
+ }
+
+ var removedProperties = [];
+ for (var i = 0; i < oldProperties.length; ++i) {
+ var oldProperty = oldProperties[i];
+
+ if (!this._properties.includes(oldProperty)) {
+ // Clear the index, since it is no longer valid.
+ oldProperty.index = NaN;
+
+ removedProperties.push(oldProperty);
+
+ // Keep around old properties in pending in case they
+ // are needed again during editing.
+ if (editable)
+ this._pendingProperties.push(oldProperty);
+ }
+ }
+
+ if (dontFireEvents)
+ return;
+
+ var addedProperties = [];
+ for (var i = 0; i < this._properties.length; ++i) {
+ if (!oldProperties.includes(this._properties[i]))
+ addedProperties.push(this._properties[i]);
+ }
+
+ // Don't fire the event if there is text and it hasn't changed.
+ if (oldText && this._text && oldText === this._text) {
+ // We shouldn't have any added or removed properties in this case.
+ console.assert(!addedProperties.length && !removedProperties.length);
+ if (!addedProperties.length && !removedProperties.length)
+ return;
+ }
+
+ function delayed()
+ {
+ this.dispatchEventToListeners(WebInspector.CSSStyleDeclaration.Event.PropertiesChanged, {addedProperties, removedProperties});
+ }
+
+ // Delay firing the PropertiesChanged event so DOMNodeStyles has a chance to mark overridden and associated properties.
+ setTimeout(delayed.bind(this), 0);
+ }
+
+ get ownerRule()
+ {
+ return this._ownerRule;
+ }
+
+ set ownerRule(rule)
+ {
+ this._ownerRule = rule || null;
+ }
+
+ get text()
+ {
+ return this._text;
+ }
+
+ set text(text)
+ {
+ if (this._text === text)
+ return;
+
+ let trimmedText = WebInspector.CSSStyleDeclarationTextEditor.PrefixWhitespace + text.trim();
+ if (this._text === trimmedText)
+ return;
+
+ if (trimmedText === WebInspector.CSSStyleDeclarationTextEditor.PrefixWhitespace || this._type === WebInspector.CSSStyleDeclaration.Type.Inline)
+ text = trimmedText;
+
+ let modified = text !== this._initialText;
+ if (modified !== this._hasModifiedInitialText) {
+ this._hasModifiedInitialText = modified;
+ this.dispatchEventToListeners(WebInspector.CSSStyleDeclaration.Event.InitialTextModified);
+ }
+
+ this._nodeStyles.changeStyleText(this, text);
+ }
+
+ resetText()
+ {
+ this.text = this._initialText;
+ }
+
+ get modified()
+ {
+ return this._hasModifiedInitialText;
+ }
+
+ get properties()
+ {
+ return this._properties;
+ }
+
+ get visibleProperties()
+ {
+ if (this._visibleProperties)
+ return this._visibleProperties;
+
+ this._visibleProperties = this._properties.filter(function(property) {
+ return !!property.styleDeclarationTextRange;
+ });
+
+ return this._visibleProperties;
+ }
+
+ get pendingProperties()
+ {
+ return this._pendingProperties;
+ }
+
+ get styleSheetTextRange()
+ {
+ return this._styleSheetTextRange;
+ }
+
+ get mediaList()
+ {
+ if (this._ownerRule)
+ return this._ownerRule.mediaList;
+ return [];
+ }
+
+ get selectorText()
+ {
+ if (this._ownerRule)
+ return this._ownerRule.selectorText;
+ return this._node.appropriateSelectorFor(true);
+ }
+
+ propertyForName(name, dontCreateIfMissing)
+ {
+ console.assert(name);
+ if (!name)
+ return null;
+
+ if (!this.editable)
+ return this._propertyNameMap[name] || null;
+
+ // Editable styles don't use the map since they need to
+ // account for overridden properties.
+
+ function findMatch(properties)
+ {
+ for (var i = 0; i < properties.length; ++i) {
+ var property = properties[i];
+ if (property.canonicalName !== name && property.name !== name)
+ continue;
+ if (bestMatchProperty && !bestMatchProperty.overridden && property.overridden)
+ continue;
+ bestMatchProperty = property;
+ }
+ }
+
+ var bestMatchProperty = null;
+
+ findMatch(this._properties);
+
+ if (bestMatchProperty)
+ return bestMatchProperty;
+
+ if (dontCreateIfMissing || !this.editable)
+ return null;
+
+ findMatch(this._pendingProperties, true);
+
+ if (bestMatchProperty)
+ return bestMatchProperty;
+
+ var newProperty = new WebInspector.CSSProperty(NaN, null, name);
+ newProperty.ownerStyle = this;
+
+ this._pendingProperties.push(newProperty);
+
+ return newProperty;
+ }
+
+ generateCSSRuleString()
+ {
+ let indentString = WebInspector.indentString();
+ let styleText = "";
+ let mediaList = this.mediaList;
+ let mediaQueriesCount = mediaList.length;
+ for (let i = mediaQueriesCount - 1; i >= 0; --i)
+ styleText += indentString.repeat(mediaQueriesCount - i - 1) + "@media " + mediaList[i].text + " {\n";
+
+ styleText += indentString.repeat(mediaQueriesCount) + this.selectorText + " {\n";
+
+ for (let property of this._properties) {
+ if (property.anonymous)
+ continue;
+
+ styleText += indentString.repeat(mediaQueriesCount + 1) + property.text.trim();
+
+ if (!styleText.endsWith(";"))
+ styleText += ";";
+
+ styleText += "\n";
+ }
+
+ for (let i = mediaQueriesCount; i > 0; --i)
+ styleText += indentString.repeat(i) + "}\n";
+
+ styleText += "}";
+
+ return styleText;
+ }
+
+ isInspectorRule()
+ {
+ return this._ownerRule && this._ownerRule.type === WebInspector.CSSStyleSheet.Type.Inspector;
+ }
+
+ hasProperties()
+ {
+ return !!this._properties.length;
+ }
+
+ // Protected
+
+ get nodeStyles()
+ {
+ return this._nodeStyles;
+ }
+};
+
+WebInspector.CSSStyleDeclaration.Event = {
+ PropertiesChanged: "css-style-declaration-properties-changed",
+ InitialTextModified: "css-style-declaration-initial-text-modified"
+};
+
+WebInspector.CSSStyleDeclaration.Type = {
+ Rule: "css-style-declaration-type-rule",
+ Inline: "css-style-declaration-type-inline",
+ Attribute: "css-style-declaration-type-attribute",
+ Computed: "css-style-declaration-type-computed"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CSSStyleSheet.js b/Source/WebInspectorUI/UserInterface/Models/CSSStyleSheet.js
new file mode 100644
index 000000000..4a64fe43b
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CSSStyleSheet.js
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CSSStyleSheet = class CSSStyleSheet extends WebInspector.SourceCode
+{
+ constructor(id)
+ {
+ super();
+
+ console.assert(id);
+
+ this._id = id || null;
+ this._url = null;
+ this._parentFrame = null;
+ this._origin = null;
+ this._startLineNumber = 0;
+ this._startColumnNumber = 0;
+
+ this._inlineStyleAttribute = false;
+ this._inlineStyleTag = false;
+
+ this._hasInfo = false;
+ }
+
+ // Static
+
+ static resetUniqueDisplayNameNumbers()
+ {
+ WebInspector.CSSStyleSheet._nextUniqueDisplayNameNumber = 1;
+ }
+
+ // Public
+
+ get id()
+ {
+ return this._id;
+ }
+
+ get parentFrame()
+ {
+ return this._parentFrame;
+ }
+
+ get origin()
+ {
+ return this._origin;
+ }
+
+ get url()
+ {
+ return this._url;
+ }
+
+ get urlComponents()
+ {
+ if (!this._urlComponents)
+ this._urlComponents = parseURL(this._url);
+ return this._urlComponents;
+ }
+
+ get mimeType()
+ {
+ return "text/css";
+ }
+
+ get displayName()
+ {
+ if (this._url)
+ return WebInspector.displayNameForURL(this._url, this.urlComponents);
+
+ // Assign a unique number to the StyleSheet object so it will stay the same.
+ if (!this._uniqueDisplayNameNumber)
+ this._uniqueDisplayNameNumber = this.constructor._nextUniqueDisplayNameNumber++;
+
+ return WebInspector.UIString("Anonymous StyleSheet %d").format(this._uniqueDisplayNameNumber);
+ }
+
+ get startLineNumber()
+ {
+ return this._startLineNumber;
+ }
+
+ get startColumnNumber()
+ {
+ return this._startColumnNumber;
+ }
+
+ hasInfo()
+ {
+ return this._hasInfo;
+ }
+
+ isInspectorStyleSheet()
+ {
+ return this._origin === WebInspector.CSSStyleSheet.Type.Inspector;
+ }
+
+ isInlineStyleTag()
+ {
+ return this._inlineStyleTag;
+ }
+
+ isInlineStyleAttributeStyleSheet()
+ {
+ return this._inlineStyleAttribute;
+ }
+
+ markAsInlineStyleAttributeStyleSheet()
+ {
+ this._inlineStyleAttribute = true;
+ }
+
+ offsetSourceCodeLocation(sourceCodeLocation)
+ {
+ if (!sourceCodeLocation)
+ return null;
+
+ if (!this._hasInfo)
+ return sourceCodeLocation;
+
+ let sourceCode = sourceCodeLocation.sourceCode;
+ let lineNumber = this._startLineNumber + sourceCodeLocation.lineNumber;
+ let columnNumber = this._startColumnNumber + sourceCodeLocation.columnNumber;
+ return sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
+ }
+
+ // Protected
+
+ updateInfo(url, parentFrame, origin, inlineStyle, startLineNumber, startColumnNumber)
+ {
+ this._hasInfo = true;
+
+ this._url = url || null;
+ this._urlComponents = undefined;
+
+ this._parentFrame = parentFrame || null;
+ this._origin = origin;
+
+ this._inlineStyleTag = inlineStyle;
+ this._startLineNumber = startLineNumber;
+ this._startColumnNumber = startColumnNumber;
+ }
+
+ get revisionForRequestedContent()
+ {
+ return this.currentRevision;
+ }
+
+ handleCurrentRevisionContentChange()
+ {
+ if (!this._id)
+ return;
+
+ function contentDidChange(error)
+ {
+ if (error)
+ return;
+
+ DOMAgent.markUndoableState();
+
+ this.dispatchEventToListeners(WebInspector.CSSStyleSheet.Event.ContentDidChange);
+ }
+
+ this._ignoreNextContentDidChangeNotification = true;
+
+ CSSAgent.setStyleSheetText(this._id, this.currentRevision.content, contentDidChange.bind(this));
+ }
+
+ requestContentFromBackend()
+ {
+ if (!this._id) {
+ // There is no identifier to request content with. Reject the promise to cause the
+ // pending callbacks to get null content.
+ return Promise.reject(new Error("There is no identifier to request content with."));
+ }
+
+ return CSSAgent.getStyleSheetText(this._id);
+ }
+
+ noteContentDidChange()
+ {
+ if (this._ignoreNextContentDidChangeNotification) {
+ this._ignoreNextContentDidChangeNotification = false;
+ return false;
+ }
+
+ this.markContentAsStale();
+ this.dispatchEventToListeners(WebInspector.CSSStyleSheet.Event.ContentDidChange);
+ return true;
+ }
+};
+
+WebInspector.CSSStyleSheet._nextUniqueDisplayNameNumber = 1;
+
+WebInspector.CSSStyleSheet.Event = {
+ ContentDidChange: "stylesheet-content-did-change"
+};
+
+WebInspector.CSSStyleSheet.Type = {
+ Author: "css-stylesheet-type-author",
+ User: "css-stylesheet-type-user",
+ UserAgent: "css-stylesheet-type-user-agent",
+ Inspector: "css-stylesheet-type-inspector"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CallFrame.js b/Source/WebInspectorUI/UserInterface/Models/CallFrame.js
new file mode 100644
index 000000000..67778a6bc
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CallFrame.js
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CallFrame = class CallFrame extends WebInspector.Object
+{
+ constructor(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted)
+ {
+ super();
+
+ console.assert(target instanceof WebInspector.Target);
+ console.assert(!sourceCodeLocation || sourceCodeLocation instanceof WebInspector.SourceCodeLocation);
+ console.assert(!thisObject || thisObject instanceof WebInspector.RemoteObject);
+ console.assert(!scopeChain || scopeChain instanceof Array);
+
+ this._target = target;
+ this._id = id || null;
+ this._sourceCodeLocation = sourceCodeLocation || null;
+ this._functionName = functionName || "";
+ this._thisObject = thisObject || null;
+ this._scopeChain = scopeChain || [];
+ this._nativeCode = nativeCode || false;
+ this._programCode = programCode || false;
+ this._isTailDeleted = isTailDeleted || false;
+ }
+
+ // Public
+
+ get target() { return this._target; }
+ get id() { return this._id; }
+ get sourceCodeLocation() { return this._sourceCodeLocation; }
+ get functionName() { return this._functionName; }
+ get nativeCode() { return this._nativeCode; }
+ get programCode() { return this._programCode; }
+ get thisObject() { return this._thisObject; }
+ get scopeChain() { return this._scopeChain; }
+ get isTailDeleted() { return this._isTailDeleted; }
+
+ saveIdentityToCookie()
+ {
+ // Do nothing. The call frame is torn down when the inspector closes, and
+ // we shouldn't restore call frame content views across debugger pauses.
+ }
+
+ collectScopeChainVariableNames(callback)
+ {
+ var result = {this: true, __proto__: null};
+
+ var pendingRequests = this._scopeChain.length;
+
+ function propertiesCollected(properties)
+ {
+ for (var i = 0; properties && i < properties.length; ++i)
+ result[properties[i].name] = true;
+
+ if (--pendingRequests)
+ return;
+
+ callback(result);
+ }
+
+ for (var i = 0; i < this._scopeChain.length; ++i)
+ this._scopeChain[i].objects[0].deprecatedGetAllProperties(propertiesCollected);
+ }
+
+ mergedScopeChain()
+ {
+ let mergedScopes = [];
+
+ // Scopes list goes from top/local (1) to bottom/global (5)
+ // [scope1, scope2, scope3, scope4, scope5]
+ let scopes = this._scopeChain.slice();
+
+ // Merge similiar scopes. Some function call frames may have multiple
+ // top level closure scopes (one for `var`s one for `let`s) that can be
+ // combined to a single scope of variables. Go in reverse order so we
+ // merge the first two closure scopes with the same name. Also mark
+ // the first time we see a new name, so we know the base for the name.
+ // [scope1&2, scope3, scope4, scope5]
+ // foo bar GLE global
+ let lastMarkedHash = null;
+ function markAsBaseIfNeeded(scope) {
+ if (!scope.hash)
+ return false;
+ if (scope.type !== WebInspector.ScopeChainNode.Type.Closure)
+ return false;
+ if (scope.hash === lastMarkedHash)
+ return false;
+ lastMarkedHash = scope.hash;
+ scope.__baseClosureScope = true;
+ return true;
+ }
+
+ function shouldMergeClosureScopes(youngScope, oldScope, lastMerge) {
+ if (!youngScope || !oldScope)
+ return false;
+
+ // Don't merge unknown locations.
+ if (!youngScope.hash || !oldScope.hash)
+ return false;
+
+ // Only merge closure scopes.
+ if (youngScope.type !== WebInspector.ScopeChainNode.Type.Closure)
+ return false;
+ if (oldScope.type !== WebInspector.ScopeChainNode.Type.Closure)
+ return false;
+
+ // Don't merge if they are not the same.
+ if (youngScope.hash !== oldScope.hash)
+ return false;
+
+ // Don't merge if there was already a merge.
+ if (lastMerge && youngScope.hash === lastMerge.hash)
+ return false;
+
+ return true;
+ }
+
+ let lastScope = null;
+ let lastMerge = null;
+ for (let i = scopes.length - 1; i >= 0; --i) {
+ let scope = scopes[i];
+ markAsBaseIfNeeded(scope);
+ if (shouldMergeClosureScopes(scope, lastScope, lastMerge)) {
+ console.assert(lastScope.__baseClosureScope);
+ let type = WebInspector.ScopeChainNode.Type.Closure;
+ let objects = lastScope.objects.concat(scope.objects);
+ let merged = new WebInspector.ScopeChainNode(type, objects, scope.name, scope.location);
+ merged.__baseClosureScope = true;
+ console.assert(objects.length === 2);
+
+ mergedScopes.pop(); // Remove the last.
+ mergedScopes.push(merged); // Add the merged scope.
+
+ lastMerge = merged;
+ lastScope = null;
+ } else {
+ mergedScopes.push(scope);
+
+ lastMerge = null;
+ lastScope = scope;
+ }
+ }
+
+ mergedScopes = mergedScopes.reverse();
+
+ // Mark the first Closure as Local if the name matches this call frame.
+ for (let scope of mergedScopes) {
+ if (scope.type === WebInspector.ScopeChainNode.Type.Closure) {
+ if (scope.name === this._functionName)
+ scope.convertToLocalScope();
+ break;
+ }
+ }
+
+ return mergedScopes;
+ }
+
+ // Static
+
+ static functionNameFromPayload(payload)
+ {
+ let functionName = payload.functionName;
+ if (functionName === "global code")
+ return WebInspector.UIString("Global Code");
+ if (functionName === "eval code")
+ return WebInspector.UIString("Eval Code");
+ if (functionName === "module code")
+ return WebInspector.UIString("Module Code");
+ return functionName;
+ }
+
+ static programCodeFromPayload(payload)
+ {
+ return payload.functionName.endsWith(" code");
+ }
+
+ static fromDebuggerPayload(target, payload, scopeChain, sourceCodeLocation)
+ {
+ let id = payload.callFrameId;
+ let thisObject = WebInspector.RemoteObject.fromPayload(payload.this, target);
+ let functionName = WebInspector.CallFrame.functionNameFromPayload(payload);
+ let nativeCode = false;
+ let programCode = WebInspector.CallFrame.programCodeFromPayload(payload);
+ let isTailDeleted = payload.isTailDeleted;
+
+ if (sourceCodeLocation && isWebInspectorConsoleEvaluationScript(sourceCodeLocation.sourceCode.sourceURL)) {
+ functionName = WebInspector.UIString("Console Evaluation");
+ programCode = true;
+ }
+
+ return new WebInspector.CallFrame(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted);
+ }
+
+ static fromPayload(target, payload)
+ {
+ console.assert(payload);
+
+ let {url, scriptId} = payload;
+ let nativeCode = false;
+ let sourceCodeLocation = null;
+ let functionName = WebInspector.CallFrame.functionNameFromPayload(payload);
+ let programCode = WebInspector.CallFrame.programCodeFromPayload(payload);
+
+ if (url === "[native code]") {
+ nativeCode = true;
+ url = null;
+ } else if (url || scriptId) {
+ let sourceCode = null;
+ if (scriptId) {
+ sourceCode = WebInspector.debuggerManager.scriptForIdentifier(scriptId, target);
+ if (sourceCode && sourceCode.resource)
+ sourceCode = sourceCode.resource;
+ }
+ if (!sourceCode)
+ sourceCode = WebInspector.frameResourceManager.resourceForURL(url);
+ if (!sourceCode)
+ sourceCode = WebInspector.debuggerManager.scriptsForURL(url, target)[0];
+
+ if (sourceCode) {
+ // The lineNumber is 1-based, but we expect 0-based.
+ let lineNumber = payload.lineNumber - 1;
+ sourceCodeLocation = sourceCode.createLazySourceCodeLocation(lineNumber, payload.columnNumber);
+ } else {
+ // Treat this as native code if we were unable to find a source.
+ console.assert(!url, "We should have detected source code for something with a url");
+ nativeCode = true;
+ url = null;
+ }
+ }
+
+ if (sourceCodeLocation && isWebInspectorConsoleEvaluationScript(sourceCodeLocation.sourceCode.sourceURL)) {
+ functionName = WebInspector.UIString("Console Evaluation");
+ programCode = true;
+ }
+
+ return new WebInspector.CallFrame(target, null, sourceCodeLocation, functionName, null, null, nativeCode, programCode);
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CallingContextTree.js b/Source/WebInspectorUI/UserInterface/Models/CallingContextTree.js
new file mode 100644
index 000000000..2fab051c0
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CallingContextTree.js
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CallingContextTree = class CallingContextTree extends WebInspector.Object
+{
+ constructor(type)
+ {
+ super();
+
+ this._type = type || WebInspector.CallingContextTree.Type.TopDown;
+
+ this.reset();
+ }
+
+ // Public
+
+ get type() { return this._type; }
+ get totalNumberOfSamples() { return this._totalNumberOfSamples; }
+
+ reset()
+ {
+ this._root = new WebInspector.CallingContextTreeNode(-1, -1, -1, "<root>", null);
+ this._totalNumberOfSamples = 0;
+ }
+
+ totalDurationInTimeRange(startTime, endTime)
+ {
+ return this._root.filteredTimestampsAndDuration(startTime, endTime).duration;
+ }
+
+ updateTreeWithStackTrace({timestamp, stackFrames}, duration)
+ {
+ this._totalNumberOfSamples++;
+
+ let node = this._root;
+ node.addTimestampAndExpressionLocation(timestamp, duration, null);
+
+ switch (this._type) {
+ case WebInspector.CallingContextTree.Type.TopDown:
+ for (let i = stackFrames.length; i--; ) {
+ let stackFrame = stackFrames[i];
+ node = node.findOrMakeChild(stackFrame);
+ node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0);
+ }
+ break;
+ case WebInspector.CallingContextTree.Type.BottomUp:
+ for (let i = 0; i < stackFrames.length; ++i) {
+ let stackFrame = stackFrames[i];
+ node = node.findOrMakeChild(stackFrame);
+ node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0);
+ }
+ break;
+ case WebInspector.CallingContextTree.Type.TopFunctionsTopDown:
+ for (let i = stackFrames.length; i--; ) {
+ node = this._root;
+ for (let j = i + 1; j--; ) {
+ let stackFrame = stackFrames[j];
+ node = node.findOrMakeChild(stackFrame);
+ node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0);
+ }
+ }
+ break;
+ case WebInspector.CallingContextTree.Type.TopFunctionsBottomUp:
+ for (let i = 0; i < stackFrames.length; i++) {
+ node = this._root;
+ for (let j = i; j < stackFrames.length; j++) {
+ let stackFrame = stackFrames[j];
+ node = node.findOrMakeChild(stackFrame);
+ node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0);
+ }
+ }
+ break;
+ default:
+ console.assert(false, "This should not be reached.");
+ break;
+ }
+ }
+
+ toCPUProfilePayload(startTime, endTime)
+ {
+ let cpuProfile = {};
+ let roots = [];
+ let numSamplesInTimeRange = this._root.filteredTimestampsAndDuration(startTime, endTime).timestamps.length;
+
+ this._root.forEachChild((child) => {
+ if (child.hasStackTraceInTimeRange(startTime, endTime))
+ roots.push(child.toCPUProfileNode(numSamplesInTimeRange, startTime, endTime));
+ });
+
+ cpuProfile.rootNodes = roots;
+ return cpuProfile;
+ }
+
+ forEachChild(callback)
+ {
+ this._root.forEachChild(callback);
+ }
+
+ forEachNode(callback)
+ {
+ this._root.forEachNode(callback);
+ }
+
+ // Testing.
+
+ static __test_makeTreeFromProtocolMessageObject(messageObject)
+ {
+ let tree = new WebInspector.CallingContextTree;
+ let stackTraces = messageObject.params.samples.stackTraces;
+ for (let i = 0; i < stackTraces.length; i++)
+ tree.updateTreeWithStackTrace(stackTraces[i]);
+ return tree;
+ }
+
+ __test_matchesStackTrace(stackTrace)
+ {
+ // StackTrace should have top frame first in the array and bottom frame last.
+ // We don't look for a match that traces down the tree from the root; instead,
+ // we match by looking at all the leafs, and matching while walking up the tree
+ // towards the root. If we successfully make the walk, we've got a match that
+ // suffices for a particular test. A successful match doesn't mean we actually
+ // walk all the way up to the root; it just means we didn't fail while walking
+ // in the direction of the root.
+ let leaves = this.__test_buildLeafLinkedLists();
+
+ outer:
+ for (let node of leaves) {
+ for (let stackNode of stackTrace) {
+ for (let propertyName of Object.getOwnPropertyNames(stackNode)) {
+ if (stackNode[propertyName] !== node[propertyName])
+ continue outer;
+ }
+ node = node.parent;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ __test_buildLeafLinkedLists()
+ {
+ let result = [];
+ let parent = null;
+ this._root.__test_buildLeafLinkedLists(parent, result);
+ return result;
+ }
+};
+
+WebInspector.CallingContextTree.Type = {
+ TopDown: Symbol("TopDown"),
+ BottomUp: Symbol("BottomUp"),
+ TopFunctionsTopDown: Symbol("TopFunctionsTopDown"),
+ TopFunctionsBottomUp: Symbol("TopFunctionsBottomUp"),
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CallingContextTreeNode.js b/Source/WebInspectorUI/UserInterface/Models/CallingContextTreeNode.js
new file mode 100644
index 000000000..db8165641
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CallingContextTreeNode.js
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CallingContextTreeNode = class CallingContextTreeNode extends WebInspector.Object
+{
+ constructor(sourceID, line, column, name, url, hash)
+ {
+ super();
+
+ this._children = {};
+ this._sourceID = sourceID;
+ this._line = line;
+ this._column = column;
+ this._name = name;
+ this._url = url;
+ this._uid = WebInspector.CallingContextTreeNode.__uid++;
+
+ this._timestamps = [];
+ this._durations = [];
+ this._leafTimestamps = [];
+ this._leafDurations = [];
+ this._expressionLocations = {}; // Keys are "line:column" strings. Values are arrays of timestamps in sorted order.
+
+ this._hash = hash || WebInspector.CallingContextTreeNode._hash(this);
+ }
+
+ // Static and Private
+
+ static _hash(stackFrame)
+ {
+ return stackFrame.name + ":" + stackFrame.sourceID + ":" + stackFrame.line + ":" + stackFrame.column;
+ }
+
+ // Public
+
+ get sourceID() { return this._sourceID; }
+ get line() { return this._line; }
+ get column() { return this._column; }
+ get name() { return this._name; }
+ get uid() { return this._uid; }
+ get url() { return this._url; }
+ get hash() { return this._hash; }
+
+ hasChildrenInTimeRange(startTime, endTime)
+ {
+ for (let propertyName of Object.getOwnPropertyNames(this._children)) {
+ let child = this._children[propertyName];
+ if (child.hasStackTraceInTimeRange(startTime, endTime))
+ return true;
+ }
+ return false;
+ }
+
+ hasStackTraceInTimeRange(startTime, endTime)
+ {
+ console.assert(startTime <= endTime);
+ if (startTime > endTime)
+ return false;
+
+ let timestamps = this._timestamps;
+ let length = timestamps.length;
+ if (!length)
+ return false;
+
+ let index = timestamps.lowerBound(startTime);
+ if (index === length)
+ return false;
+ console.assert(startTime <= timestamps[index]);
+
+ let hasTimestampInRange = timestamps[index] <= endTime;
+ return hasTimestampInRange;
+ }
+
+ filteredTimestampsAndDuration(startTime, endTime)
+ {
+ let lowerIndex = this._timestamps.lowerBound(startTime);
+ let upperIndex = this._timestamps.upperBound(endTime);
+
+ let totalDuration = 0;
+ for (let i = lowerIndex; i < upperIndex; ++i)
+ totalDuration += this._durations[i];
+
+ return {
+ timestamps: this._timestamps.slice(lowerIndex, upperIndex),
+ duration: totalDuration,
+ };
+ }
+
+ filteredLeafTimestampsAndDuration(startTime, endTime)
+ {
+ let lowerIndex = this._leafTimestamps.lowerBound(startTime);
+ let upperIndex = this._leafTimestamps.upperBound(endTime);
+
+ let totalDuration = 0;
+ for (let i = lowerIndex; i < upperIndex; ++i)
+ totalDuration += this._leafDurations[i];
+
+ return {
+ leafTimestamps: this._leafTimestamps.slice(lowerIndex, upperIndex),
+ leafDuration: totalDuration,
+ };
+ }
+
+ hasChildren()
+ {
+ return !isEmptyObject(this._children);
+ }
+
+ findOrMakeChild(stackFrame)
+ {
+ let hash = WebInspector.CallingContextTreeNode._hash(stackFrame);
+ let node = this._children[hash];
+ if (node)
+ return node;
+ node = new WebInspector.CallingContextTreeNode(stackFrame.sourceID, stackFrame.line, stackFrame.column, stackFrame.name, stackFrame.url, hash);
+ this._children[hash] = node;
+ return node;
+ }
+
+ addTimestampAndExpressionLocation(timestamp, duration, expressionLocation, leaf)
+ {
+ console.assert(!this._timestamps.length || this._timestamps.lastValue <= timestamp, "Expected timestamps to be added in sorted, increasing, order.");
+ this._timestamps.push(timestamp);
+ this._durations.push(duration);
+
+ if (leaf) {
+ this._leafTimestamps.push(timestamp);
+ this._leafDurations.push(duration);
+ }
+
+ if (!expressionLocation)
+ return;
+
+ let {line, column} = expressionLocation;
+ let hashCons = line + ":" + column;
+ let timestamps = this._expressionLocations[hashCons];
+ if (!timestamps) {
+ timestamps = [];
+ this._expressionLocations[hashCons] = timestamps;
+ }
+ console.assert(!timestamps.length || timestamps.lastValue <= timestamp, "Expected timestamps to be added in sorted, increasing, order.");
+ timestamps.push(timestamp);
+ }
+
+ forEachChild(callback)
+ {
+ for (let propertyName of Object.getOwnPropertyNames(this._children))
+ callback(this._children[propertyName]);
+ }
+
+ forEachNode(callback)
+ {
+ callback(this);
+ this.forEachChild(function(child) {
+ child.forEachNode(callback);
+ });
+ }
+
+ equals(other)
+ {
+ return this._hash === other.hash;
+ }
+
+ toCPUProfileNode(numSamples, startTime, endTime)
+ {
+ let children = [];
+ this.forEachChild((child) => {
+ if (child.hasStackTraceInTimeRange(startTime, endTime))
+ children.push(child.toCPUProfileNode(numSamples, startTime, endTime));
+ });
+ let cpuProfileNode = {
+ id: this._uid,
+ functionName: this._name,
+ url: this._url,
+ lineNumber: this._line,
+ columnNumber: this._column,
+ children: children
+ };
+
+ let timestamps = [];
+ let frameStartTime = Number.MAX_VALUE;
+ let frameEndTime = Number.MIN_VALUE;
+ for (let i = 0; i < this._timestamps.length; i++) {
+ let timestamp = this._timestamps[i];
+ if (startTime <= timestamp && timestamp <= endTime) {
+ timestamps.push(timestamp);
+ frameStartTime = Math.min(frameStartTime, timestamp);
+ frameEndTime = Math.max(frameEndTime, timestamp);
+ }
+ }
+
+ cpuProfileNode.callInfo = {
+ callCount: timestamps.length, // Totally not callCount, but oh well, this makes life easier because of field names.
+ startTime: frameStartTime,
+ endTime: frameEndTime,
+ totalTime: (timestamps.length / numSamples) * (endTime - startTime)
+ };
+
+ return cpuProfileNode;
+ }
+
+ // Testing.
+
+ __test_buildLeafLinkedLists(parent, result)
+ {
+ let linkedListNode = {
+ name: this._name,
+ url: this._url,
+ parent: parent
+ };
+ if (this.hasChildren()) {
+ this.forEachChild((child) => {
+ child.__test_buildLeafLinkedLists(linkedListNode, result);
+ });
+ } else {
+ // We're a leaf.
+ result.push(linkedListNode);
+ }
+ }
+};
+
+WebInspector.CallingContextTreeNode.__uid = 0;
diff --git a/Source/WebInspectorUI/UserInterface/Models/Collection.js b/Source/WebInspectorUI/UserInterface/Models/Collection.js
new file mode 100644
index 000000000..8c7739f83
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Collection.js
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 Devin Rousso <dcrousso+webkit@gmail.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Collection = class Collection extends WebInspector.Object
+{
+ constructor(typeVerifier)
+ {
+ super();
+
+ this._items = new Set;
+
+ console.assert(!typeVerifier || typeof typeVerifier === "function");
+ this._typeVerifier = typeVerifier || WebInspector.Collection.TypeVerifier.Any;
+ }
+
+ // Public
+
+ get items() { return this._items; }
+ get typeVerifier() { return this._typeVerifier; }
+
+ add(item)
+ {
+ let isValidType = this._typeVerifier(item);
+ console.assert(isValidType);
+ if (!isValidType)
+ return;
+
+ console.assert(!this._items.has(item));
+ this._items.add(item);
+
+ this.itemAdded(item);
+
+ this.dispatchEventToListeners(WebInspector.Collection.Event.ItemAdded, {item});
+ }
+
+ remove(item)
+ {
+ let wasRemoved = this._items.delete(item);
+ console.assert(wasRemoved);
+
+ this.itemRemoved(item);
+
+ this.dispatchEventToListeners(WebInspector.Collection.Event.ItemRemoved, {item});
+ }
+
+ clear()
+ {
+ let items = new Set(this._items);
+
+ this._items.clear();
+
+ this.itemsCleared(items);
+
+ for (let item of items)
+ this.dispatchEventToListeners(WebInspector.Collection.Event.ItemRemoved, {item});
+ }
+
+ toArray()
+ {
+ return Array.from(this._items);
+ }
+
+ toJSON()
+ {
+ return this.toArray();
+ }
+
+ // Protected
+
+ itemAdded(item)
+ {
+ // Implemented by subclasses.
+ }
+
+ itemRemoved(item)
+ {
+ // Implemented by subclasses.
+ }
+
+ itemsCleared(items)
+ {
+ // Implemented by subclasses.
+ }
+};
+
+ WebInspector.Collection.Event = {
+ ItemAdded: "collection-item-added",
+ ItemRemoved: "collection-item-removed",
+};
+
+ WebInspector.Collection.TypeVerifier = {
+ Any: (object) => true,
+ ContentFlow: (object) => object instanceof WebInspector.ContentFlow,
+ Frame: (object) => object instanceof WebInspector.Frame,
+ Resource: (object) => object instanceof WebInspector.Resource,
+ Script: (object) => object instanceof WebInspector.Script,
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CollectionEntry.js b/Source/WebInspectorUI/UserInterface/Models/CollectionEntry.js
new file mode 100644
index 000000000..681e0a4a7
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CollectionEntry.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CollectionEntry = class CollectionEntry extends WebInspector.Object
+{
+ constructor(key, value)
+ {
+ super();
+
+ console.assert(value instanceof WebInspector.RemoteObject);
+ console.assert(!key || key instanceof WebInspector.RemoteObject);
+
+ this._key = key;
+ this._value = value;
+ }
+
+ // Static
+
+ // Runtime.CollectionEntry.
+ static fromPayload(payload, target)
+ {
+ if (payload.key)
+ payload.key = WebInspector.RemoteObject.fromPayload(payload.key, target);
+ if (payload.value)
+ payload.value = WebInspector.RemoteObject.fromPayload(payload.value, target);
+
+ return new WebInspector.CollectionEntry(payload.key, payload.value);
+ }
+
+ // Public
+
+ get key() { return this._key; }
+ get value() { return this._value; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CollectionEntryPreview.js b/Source/WebInspectorUI/UserInterface/Models/CollectionEntryPreview.js
new file mode 100644
index 000000000..d88ad8230
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CollectionEntryPreview.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CollectionEntryPreview = class CollectionEntryPreview extends WebInspector.Object
+{
+ constructor(keyPreview, valuePreview)
+ {
+ super();
+
+ console.assert(valuePreview instanceof WebInspector.ObjectPreview);
+ console.assert(!keyPreview || keyPreview instanceof WebInspector.ObjectPreview);
+
+ this._key = keyPreview;
+ this._value = valuePreview;
+ }
+
+ // Static
+
+ // Runtime.EntryPreview.
+ static fromPayload(payload)
+ {
+ if (payload.key)
+ payload.key = WebInspector.ObjectPreview.fromPayload(payload.key);
+ if (payload.value)
+ payload.value = WebInspector.ObjectPreview.fromPayload(payload.value);
+
+ return new WebInspector.CollectionEntryPreview(payload.key, payload.value);
+ }
+
+ // Public
+
+ get keyPreview() { return this._key; }
+ get valuePreview() { return this._value; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Color.js b/Source/WebInspectorUI/UserInterface/Models/Color.js
new file mode 100644
index 000000000..491bf2870
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Color.js
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Joseph Pecoraro
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Color = class Color
+{
+ constructor(format, components)
+ {
+ this.format = format;
+
+ if (format === WebInspector.Color.Format.HSL || format === WebInspector.Color.Format.HSLA)
+ this._hsla = components;
+ else
+ this._rgba = components;
+
+ this.valid = !components.some(isNaN);
+ }
+
+ // Static
+
+ static fromString(colorString)
+ {
+ let value = colorString.toLowerCase().replace(/%|\s+/g, "");
+ let transparentKeywords = ["transparent", "rgba(0,0,0,0)", "hsla(0,0,0,0)"];
+ if (transparentKeywords.includes(value)) {
+ let color = new WebInspector.Color(WebInspector.Color.Format.Keyword, [0, 0, 0, 0]);
+ color.keyword = "transparent";
+ color.original = colorString;
+ return color;
+ }
+
+ // Simple - #hex, rgb(), keyword, hsl()
+ let simple = /^(?:#([0-9a-f]{3,8})|rgb\(([^)]+)\)|(\w+)|hsl\(([^)]+)\))$/i;
+ let match = colorString.match(simple);
+ if (match) {
+ if (match[1]) { // hex
+ let hex = match[1].toUpperCase();
+ let len = hex.length;
+ if (len === 3) {
+ return new WebInspector.Color(WebInspector.Color.Format.ShortHEX, [
+ parseInt(hex.charAt(0) + hex.charAt(0), 16),
+ parseInt(hex.charAt(1) + hex.charAt(1), 16),
+ parseInt(hex.charAt(2) + hex.charAt(2), 16),
+ 1
+ ]);
+ } else if (len === 6) {
+ return new WebInspector.Color(WebInspector.Color.Format.HEX, [
+ parseInt(hex.substring(0, 2), 16),
+ parseInt(hex.substring(2, 4), 16),
+ parseInt(hex.substring(4, 6), 16),
+ 1
+ ]);
+ } else if (len === 4) {
+ return new WebInspector.Color(WebInspector.Color.Format.ShortHEXAlpha, [
+ parseInt(hex.charAt(0) + hex.charAt(0), 16),
+ parseInt(hex.charAt(1) + hex.charAt(1), 16),
+ parseInt(hex.charAt(2) + hex.charAt(2), 16),
+ parseInt(hex.charAt(3) + hex.charAt(3), 16) / 255
+ ]);
+ } else if (len === 8) {
+ return new WebInspector.Color(WebInspector.Color.Format.HEXAlpha, [
+ parseInt(hex.substring(0, 2), 16),
+ parseInt(hex.substring(2, 4), 16),
+ parseInt(hex.substring(4, 6), 16),
+ parseInt(hex.substring(6, 8), 16) / 255
+ ]);
+ } else
+ return null;
+ } else if (match[2]) { // rgb
+ let rgb = match[2].split(/\s*,\s*/);
+ if (rgb.length !== 3)
+ return null;
+ return new WebInspector.Color(WebInspector.Color.Format.RGB, [
+ parseInt(rgb[0]),
+ parseInt(rgb[1]),
+ parseInt(rgb[2]),
+ 1
+ ]);
+ } else if (match[3]) { // keyword
+ let keyword = match[3].toLowerCase();
+ if (!WebInspector.Color.Keywords.hasOwnProperty(keyword))
+ return null;
+ let color = new WebInspector.Color(WebInspector.Color.Format.Keyword, WebInspector.Color.Keywords[keyword].concat(1));
+ color.keyword = keyword;
+ color.original = colorString;
+ return color;
+ } else if (match[4]) { // hsl
+ let hsl = match[4].replace(/%/g, "").split(/\s*,\s*/);
+ if (hsl.length !== 3)
+ return null;
+ return new WebInspector.Color(WebInspector.Color.Format.HSL, [
+ parseInt(hsl[0]),
+ parseInt(hsl[1]),
+ parseInt(hsl[2]),
+ 1
+ ]);
+ }
+ }
+
+ // Advanced - rgba(), hsla()
+ let advanced = /^(?:rgba\(([^)]+)\)|hsla\(([^)]+)\))$/i;
+ match = colorString.match(advanced);
+ if (match) {
+ if (match[1]) { // rgba
+ let rgba = match[1].split(/\s*,\s*/);
+ if (rgba.length !== 4)
+ return null;
+ return new WebInspector.Color(WebInspector.Color.Format.RGBA, [
+ parseInt(rgba[0]),
+ parseInt(rgba[1]),
+ parseInt(rgba[2]),
+ Number.constrain(parseFloat(rgba[3]), 0, 1)
+ ]);
+ } else if (match[2]) { // hsla
+ let hsla = match[2].replace(/%/g, "").split(/\s*,\s*/);
+ if (hsla.length !== 4)
+ return null;
+ return new WebInspector.Color(WebInspector.Color.Format.HSLA, [
+ parseInt(hsla[0]),
+ parseInt(hsla[1]),
+ parseInt(hsla[2]),
+ Number.constrain(parseFloat(hsla[3]), 0, 1)
+ ]);
+ }
+ }
+
+ return null;
+ }
+
+ static rgb2hsv(r, g, b)
+ {
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ let min = Math.min(Math.min(r, g), b);
+ let max = Math.max(Math.max(r, g), b);
+ let delta = max - min;
+
+ let h;
+ let s;
+ let v = max;
+
+ if (delta === 0)
+ h = 0;
+ else if (max === r)
+ h = (60 * ((g - b) / delta)) % 360;
+ else if (max === g)
+ h = 60 * ((b - r) / delta) + 120;
+ else if (max === b)
+ h = 60 * ((r - g) / delta) + 240;
+
+ if (h < 0)
+ h += 360;
+
+ // Saturation
+ if (max === 0)
+ s = 0;
+ else
+ s = 1 - (min / max);
+
+ return [h, s, v];
+ }
+
+ static hsv2rgb(h, s, v)
+ {
+ if (s === 0)
+ return [v, v, v];
+
+ h /= 60;
+ let i = Math.floor(h);
+ let data = [
+ v * (1 - s),
+ v * (1 - s * (h - i)),
+ v * (1 - s * (1 - (h - i)))
+ ];
+ let rgb;
+
+ switch (i) {
+ case 0:
+ rgb = [v, data[2], data[0]];
+ break;
+ case 1:
+ rgb = [data[1], v, data[0]];
+ break;
+ case 2:
+ rgb = [data[0], v, data[2]];
+ break;
+ case 3:
+ rgb = [data[0], data[1], v];
+ break;
+ case 4:
+ rgb = [data[2], data[0], v];
+ break;
+ default:
+ rgb = [v, data[0], data[1]];
+ break;
+ }
+
+ return rgb;
+ }
+
+
+ // Public
+
+ nextFormat(format)
+ {
+ format = format || this.format;
+
+ switch (format) {
+ case WebInspector.Color.Format.Original:
+ case WebInspector.Color.Format.HEX:
+ case WebInspector.Color.Format.HEXAlpha:
+ return this.simple ? WebInspector.Color.Format.RGB : WebInspector.Color.Format.RGBA;
+
+ case WebInspector.Color.Format.RGB:
+ case WebInspector.Color.Format.RGBA:
+ return this.simple ? WebInspector.Color.Format.HSL : WebInspector.Color.Format.HSLA;
+
+ case WebInspector.Color.Format.HSL:
+ case WebInspector.Color.Format.HSLA:
+ if (this.isKeyword())
+ return WebInspector.Color.Format.Keyword;
+ if (this.simple)
+ return this.canBeSerializedAsShortHEX() ? WebInspector.Color.Format.ShortHEX : WebInspector.Color.Format.HEX;
+ return this.canBeSerializedAsShortHEX() ? WebInspector.Color.Format.ShortHEXAlpha : WebInspector.Color.Format.HEXAlpha;
+
+ case WebInspector.Color.Format.ShortHEX:
+ return WebInspector.Color.Format.HEX;
+
+ case WebInspector.Color.Format.ShortHEXAlpha:
+ return WebInspector.Color.Format.HEXAlpha;
+
+ case WebInspector.Color.Format.Keyword:
+ if (this.simple)
+ return this.canBeSerializedAsShortHEX() ? WebInspector.Color.Format.ShortHEX : WebInspector.Color.Format.HEX;
+ return this.canBeSerializedAsShortHEX() ? WebInspector.Color.Format.ShortHEXAlpha : WebInspector.Color.Format.HEXAlpha;
+
+ default:
+ console.error("Unknown color format.");
+ return null;
+ }
+ }
+
+ get alpha()
+ {
+ return this._rgba ? this._rgba[3] : this._hsla[3];
+ }
+
+ get simple()
+ {
+ return this.alpha === 1;
+ }
+
+ get rgb()
+ {
+ let rgb = this.rgba.slice();
+ rgb.pop();
+ return rgb;
+ }
+
+ get hsl()
+ {
+ let hsl = this.hsla.slice();
+ hsl.pop();
+ return hsl;
+ }
+
+ get rgba()
+ {
+ if (!this._rgba)
+ this._rgba = this._hslaToRGBA(this._hsla);
+ return this._rgba;
+ }
+
+ get hsla()
+ {
+ if (!this._hsla)
+ this._hsla = this._rgbaToHSLA(this.rgba);
+ return this._hsla;
+ }
+
+ copy()
+ {
+ switch (this.format) {
+ case WebInspector.Color.Format.RGB:
+ case WebInspector.Color.Format.HEX:
+ case WebInspector.Color.Format.ShortHEX:
+ case WebInspector.Color.Format.HEXAlpha:
+ case WebInspector.Color.Format.ShortHEXAlpha:
+ case WebInspector.Color.Format.Keyword:
+ case WebInspector.Color.Format.RGBA:
+ return new WebInspector.Color(this.format, this.rgba);
+ case WebInspector.Color.Format.HSL:
+ case WebInspector.Color.Format.HSLA:
+ return new WebInspector.Color(this.format, this.hsla);
+ }
+ }
+
+ toString(format)
+ {
+ if (!format)
+ format = this.format;
+
+ switch (format) {
+ case WebInspector.Color.Format.Original:
+ return this._toOriginalString();
+ case WebInspector.Color.Format.RGB:
+ return this._toRGBString();
+ case WebInspector.Color.Format.RGBA:
+ return this._toRGBAString();
+ case WebInspector.Color.Format.HSL:
+ return this._toHSLString();
+ case WebInspector.Color.Format.HSLA:
+ return this._toHSLAString();
+ case WebInspector.Color.Format.HEX:
+ return this._toHEXString();
+ case WebInspector.Color.Format.ShortHEX:
+ return this._toShortHEXString();
+ case WebInspector.Color.Format.HEXAlpha:
+ return this._toHEXAlphaString();
+ case WebInspector.Color.Format.ShortHEXAlpha:
+ return this._toShortHEXAlphaString();
+ case WebInspector.Color.Format.Keyword:
+ return this._toKeywordString();
+ }
+
+ throw "invalid color format";
+ }
+
+ isKeyword()
+ {
+ if (this.keyword)
+ return true;
+
+ if (!this.simple)
+ return Array.shallowEqual(this._rgba, [0, 0, 0, 0]) || Array.shallowEqual(this._hsla, [0, 0, 0, 0]);
+
+ let rgb = (this._rgba && this._rgba.slice(0, 3)) || this._hslToRGB(this._hsla);
+ return Object.keys(WebInspector.Color.Keywords).some(key => Array.shallowEqual(WebInspector.Color.Keywords[key], rgb));
+ }
+
+ canBeSerializedAsShortHEX()
+ {
+ let rgba = this.rgba || this._hslaToRGBA(this._hsla);
+
+ let r = this._componentToHexValue(rgba[0]);
+ if (r[0] !== r[1])
+ return false;
+
+ let g = this._componentToHexValue(rgba[1]);
+ if (g[0] !== g[1])
+ return false;
+
+ let b = this._componentToHexValue(rgba[2]);
+ if (b[0] !== b[1])
+ return false;
+
+ if (!this.simple) {
+ let a = this._componentToHexValue(Math.round(rgba[3] * 255));
+ if (a[0] !== a[1])
+ return false;
+ }
+
+ return true;
+ }
+
+ // Private
+
+ _toOriginalString()
+ {
+ return this.original || this._toKeywordString();
+ }
+
+ _toKeywordString()
+ {
+ if (this.keyword)
+ return this.keyword;
+
+ let rgba = this.rgba;
+ if (!this.simple) {
+ if (rgba[0] === 0 && rgba[1] === 0 && rgba[2] === 0 && rgba[3] === 0)
+ return "transparent";
+ return this._toRGBAString();
+ }
+
+ let keywords = WebInspector.Color.Keywords;
+ for (let keyword in keywords) {
+ if (!keywords.hasOwnProperty(keyword))
+ continue;
+
+ let keywordRGB = keywords[keyword];
+ if (keywordRGB[0] === rgba[0] && keywordRGB[1] === rgba[1] && keywordRGB[2] === rgba[2])
+ return keyword;
+ }
+
+ return this._toRGBString();
+ }
+
+ _toShortHEXString()
+ {
+ if (!this.simple)
+ return this._toRGBAString();
+
+ let rgba = this.rgba;
+ let r = this._componentToHexValue(rgba[0]);
+ let g = this._componentToHexValue(rgba[1]);
+ let b = this._componentToHexValue(rgba[2]);
+
+ if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1])
+ return "#" + r[0] + g[0] + b[0];
+ else
+ return "#" + r + g + b;
+ }
+
+ _toHEXString()
+ {
+ if (!this.simple)
+ return this._toRGBAString();
+
+ let rgba = this.rgba;
+ let r = this._componentToHexValue(rgba[0]);
+ let g = this._componentToHexValue(rgba[1]);
+ let b = this._componentToHexValue(rgba[2]);
+
+ return "#" + r + g + b;
+ }
+
+ _toShortHEXAlphaString()
+ {
+ let rgba = this.rgba;
+ let r = this._componentToHexValue(rgba[0]);
+ let g = this._componentToHexValue(rgba[1]);
+ let b = this._componentToHexValue(rgba[2]);
+ let a = this._componentToHexValue(Math.round(rgba[3] * 255));
+
+ if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1] && a[0] === a[1])
+ return "#" + r[0] + g[0] + b[0] + a[0];
+ else
+ return "#" + r + g + b + a;
+ }
+
+ _toHEXAlphaString()
+ {
+ let rgba = this.rgba;
+ let r = this._componentToHexValue(rgba[0]);
+ let g = this._componentToHexValue(rgba[1]);
+ let b = this._componentToHexValue(rgba[2]);
+ let a = this._componentToHexValue(Math.round(rgba[3] * 255));
+
+ return "#" + r + g + b + a;
+ }
+
+ _toRGBString()
+ {
+ if (!this.simple)
+ return this._toRGBAString();
+
+ let rgba = this.rgba.slice(0, -1);
+ rgba = rgba.map((value) => value.maxDecimals(2));
+ return "rgb(" + rgba.join(", ") + ")";
+ }
+
+ _toRGBAString()
+ {
+ let rgba = this.rgba;
+ rgba = rgba.map((value) => value.maxDecimals(2));
+ return "rgba(" + rgba.join(", ") + ")";
+ }
+
+ _toHSLString()
+ {
+ if (!this.simple)
+ return this._toHSLAString();
+
+ let hsla = this.hsla;
+ hsla = hsla.map((value) => value.maxDecimals(2));
+ return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
+ }
+
+ _toHSLAString()
+ {
+ let hsla = this.hsla;
+ hsla = hsla.map((value) => value.maxDecimals(2));
+ return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + hsla[3] + ")";
+ }
+
+ _componentToNumber(value)
+ {
+ return Number.constrain(value, 0, 255);
+ }
+
+ _componentToHexValue(value)
+ {
+ let hex = this._componentToNumber(value).toString(16);
+ if (hex.length === 1)
+ hex = "0" + hex;
+ return hex;
+ }
+
+ _rgbToHSL(rgb)
+ {
+ let r = this._componentToNumber(rgb[0]) / 255;
+ let g = this._componentToNumber(rgb[1]) / 255;
+ let b = this._componentToNumber(rgb[2]) / 255;
+ let max = Math.max(r, g, b);
+ let min = Math.min(r, g, b);
+ let diff = max - min;
+ let add = max + min;
+
+ let h;
+ let s;
+ let l = 0.5 * add;
+
+ if (min === max)
+ h = 0;
+ else if (r === max)
+ h = ((60 * (g - b) / diff) + 360) % 360;
+ else if (g === max)
+ h = (60 * (b - r) / diff) + 120;
+ else
+ h = (60 * (r - g) / diff) + 240;
+
+ if (l === 0)
+ s = 0;
+ else if (l === 1)
+ s = 1;
+ else if (l <= 0.5)
+ s = diff / add;
+ else
+ s = diff / (2 - add);
+
+ return [
+ Math.round(h),
+ Math.round(s * 100),
+ Math.round(l * 100)
+ ];
+ }
+
+ _hslToRGB(hsl)
+ {
+ let h = parseFloat(hsl[0]) / 360;
+ let s = parseFloat(hsl[1]) / 100;
+ let l = parseFloat(hsl[2]) / 100;
+
+ h *= 6;
+ let sArray = [
+ l += s *= l < .5 ? l : 1 - l,
+ l - h % 1 * s * 2,
+ l -= s *= 2,
+ l,
+ l + h % 1 * s,
+ l + s
+ ];
+ return [
+ Math.round(sArray[ ~~h % 6 ] * 255),
+ Math.round(sArray[ (h | 16) % 6 ] * 255),
+ Math.round(sArray[ (h | 8) % 6 ] * 255)
+ ];
+ }
+
+ _rgbaToHSLA(rgba)
+ {
+ let hsl = this._rgbToHSL(rgba);
+ hsl.push(rgba[3]);
+ return hsl;
+ }
+
+ _hslaToRGBA(hsla)
+ {
+ let rgba = this._hslToRGB(hsla);
+ rgba.push(hsla[3]);
+ return rgba;
+ }
+};
+
+WebInspector.Color.Format = {
+ Original: "color-format-original",
+ Keyword: "color-format-keyword",
+ HEX: "color-format-hex",
+ ShortHEX: "color-format-short-hex",
+ HEXAlpha: "color-format-hex-alpha",
+ ShortHEXAlpha: "color-format-short-hex-alpha",
+ RGB: "color-format-rgb",
+ RGBA: "color-format-rgba",
+ HSL: "color-format-hsl",
+ HSLA: "color-format-hsla"
+};
+
+WebInspector.Color.Keywords = {
+ "aliceblue": [240, 248, 255],
+ "antiquewhite": [250, 235, 215],
+ "aquamarine": [127, 255, 212],
+ "azure": [240, 255, 255],
+ "beige": [245, 245, 220],
+ "bisque": [255, 228, 196],
+ "black": [0, 0, 0],
+ "blanchedalmond": [255, 235, 205],
+ "blue": [0, 0, 255],
+ "blueviolet": [138, 43, 226],
+ "brown": [165, 42, 42],
+ "burlywood": [222, 184, 135],
+ "cadetblue": [95, 158, 160],
+ "chartreuse": [127, 255, 0],
+ "chocolate": [210, 105, 30],
+ "coral": [255, 127, 80],
+ "cornflowerblue": [100, 149, 237],
+ "cornsilk": [255, 248, 220],
+ "crimson": [237, 164, 61],
+ "cyan": [0, 255, 255],
+ "darkblue": [0, 0, 139],
+ "darkcyan": [0, 139, 139],
+ "darkgoldenrod": [184, 134, 11],
+ "darkgray": [169, 169, 169],
+ "darkgreen": [0, 100, 0],
+ "darkgrey": [169, 169, 169],
+ "darkkhaki": [189, 183, 107],
+ "darkmagenta": [139, 0, 139],
+ "darkolivegreen": [85, 107, 47],
+ "darkorange": [255, 140, 0],
+ "darkorchid": [153, 50, 204],
+ "darkred": [139, 0, 0],
+ "darksalmon": [233, 150, 122],
+ "darkseagreen": [143, 188, 143],
+ "darkslateblue": [72, 61, 139],
+ "darkslategray": [47, 79, 79],
+ "darkslategrey": [47, 79, 79],
+ "darkturquoise": [0, 206, 209],
+ "darkviolet": [148, 0, 211],
+ "deeppink": [255, 20, 147],
+ "deepskyblue": [0, 191, 255],
+ "dimgray": [105, 105, 105],
+ "dimgrey": [105, 105, 105],
+ "dodgerblue": [30, 144, 255],
+ "firebrick": [178, 34, 34],
+ "floralwhite": [255, 250, 240],
+ "forestgreen": [34, 139, 34],
+ "gainsboro": [220, 220, 220],
+ "ghostwhite": [248, 248, 255],
+ "gold": [255, 215, 0],
+ "goldenrod": [218, 165, 32],
+ "gray": [128, 128, 128],
+ "green": [0, 128, 0],
+ "greenyellow": [173, 255, 47],
+ "grey": [128, 128, 128],
+ "honeydew": [240, 255, 240],
+ "hotpink": [255, 105, 180],
+ "indianred": [205, 92, 92],
+ "indigo": [75, 0, 130],
+ "ivory": [255, 255, 240],
+ "khaki": [240, 230, 140],
+ "lavender": [230, 230, 250],
+ "lavenderblush": [255, 240, 245],
+ "lawngreen": [124, 252, 0],
+ "lemonchiffon": [255, 250, 205],
+ "lightblue": [173, 216, 230],
+ "lightcoral": [240, 128, 128],
+ "lightcyan": [224, 255, 255],
+ "lightgoldenrodyellow": [250, 250, 210],
+ "lightgray": [211, 211, 211],
+ "lightgreen": [144, 238, 144],
+ "lightgrey": [211, 211, 211],
+ "lightpink": [255, 182, 193],
+ "lightsalmon": [255, 160, 122],
+ "lightseagreen": [32, 178, 170],
+ "lightskyblue": [135, 206, 250],
+ "lightslategray": [119, 136, 153],
+ "lightslategrey": [119, 136, 153],
+ "lightsteelblue": [176, 196, 222],
+ "lightyellow": [255, 255, 224],
+ "lime": [0, 255, 0],
+ "limegreen": [50, 205, 50],
+ "linen": [250, 240, 230],
+ "magenta": [255, 0, 255],
+ "maroon": [128, 0, 0],
+ "mediumaquamarine": [102, 205, 170],
+ "mediumblue": [0, 0, 205],
+ "mediumorchid": [186, 85, 211],
+ "mediumpurple": [147, 112, 219],
+ "mediumseagreen": [60, 179, 113],
+ "mediumslateblue": [123, 104, 238],
+ "mediumspringgreen": [0, 250, 154],
+ "mediumturquoise": [72, 209, 204],
+ "mediumvioletred": [199, 21, 133],
+ "midnightblue": [25, 25, 112],
+ "mintcream": [245, 255, 250],
+ "mistyrose": [255, 228, 225],
+ "moccasin": [255, 228, 181],
+ "navajowhite": [255, 222, 173],
+ "navy": [0, 0, 128],
+ "oldlace": [253, 245, 230],
+ "olive": [128, 128, 0],
+ "olivedrab": [107, 142, 35],
+ "orange": [255, 165, 0],
+ "orangered": [255, 69, 0],
+ "orchid": [218, 112, 214],
+ "palegoldenrod": [238, 232, 170],
+ "palegreen": [152, 251, 152],
+ "paleturquoise": [175, 238, 238],
+ "palevioletred": [219, 112, 147],
+ "papayawhip": [255, 239, 213],
+ "peachpuff": [255, 218, 185],
+ "peru": [205, 133, 63],
+ "pink": [255, 192, 203],
+ "plum": [221, 160, 221],
+ "powderblue": [176, 224, 230],
+ "purple": [128, 0, 128],
+ "rebeccapurple": [102, 51, 153],
+ "red": [255, 0, 0],
+ "rosybrown": [188, 143, 143],
+ "royalblue": [65, 105, 225],
+ "saddlebrown": [139, 69, 19],
+ "salmon": [250, 128, 114],
+ "sandybrown": [244, 164, 96],
+ "seagreen": [46, 139, 87],
+ "seashell": [255, 245, 238],
+ "sienna": [160, 82, 45],
+ "silver": [192, 192, 192],
+ "skyblue": [135, 206, 235],
+ "slateblue": [106, 90, 205],
+ "slategray": [112, 128, 144],
+ "slategrey": [112, 128, 144],
+ "snow": [255, 250, 250],
+ "springgreen": [0, 255, 127],
+ "steelblue": [70, 130, 180],
+ "tan": [210, 180, 140],
+ "teal": [0, 128, 128],
+ "thistle": [216, 191, 216],
+ "tomato": [255, 99, 71],
+ "turquoise": [64, 224, 208],
+ "violet": [238, 130, 238],
+ "wheat": [245, 222, 179],
+ "white": [255, 255, 255],
+ "whitesmoke": [245, 245, 245],
+ "yellow": [255, 255, 0],
+ "yellowgreen": [154, 205, 50]
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ConsoleCommandResultMessage.js b/Source/WebInspectorUI/UserInterface/Models/ConsoleCommandResultMessage.js
new file mode 100644
index 000000000..f76c8a82c
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ConsoleCommandResultMessage.js
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Joseph Pecoraro
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ConsoleCommandResultMessage = class ConsoleCommandResult extends WebInspector.ConsoleMessage
+{
+ constructor(target, result, wasThrown, savedResultIndex, shouldRevealConsole = true)
+ {
+ let source = WebInspector.ConsoleMessage.MessageSource.JS;
+ let level = wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
+ let type = WebInspector.ConsoleMessage.MessageType.Result;
+
+ super(target, source, level, "", type, undefined, undefined, undefined, 0, [result], undefined, undefined);
+
+ this._savedResultIndex = savedResultIndex;
+ this._shouldRevealConsole = shouldRevealConsole;
+
+ if (this._savedResultIndex && this._savedResultIndex > WebInspector.ConsoleCommandResultMessage.maximumSavedResultIndex)
+ WebInspector.ConsoleCommandResultMessage.maximumSavedResultIndex = this._savedResultIndex;
+ }
+
+ // Static
+
+ static clearMaximumSavedResultIndex()
+ {
+ WebInspector.ConsoleCommandResultMessage.maximumSavedResultIndex = 0;
+ }
+
+ // Public
+
+ get savedResultIndex()
+ {
+ return this._savedResultIndex;
+ }
+
+ get shouldRevealConsole()
+ {
+ return this._shouldRevealConsole;
+ }
+};
+
+WebInspector.ConsoleCommandResultMessage.maximumSavedResultIndex = 0;
diff --git a/Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js b/Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js
new file mode 100644
index 000000000..b8154ae06
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ConsoleMessage = class ConsoleMessage extends WebInspector.Object
+{
+ constructor(target, source, level, message, type, url, line, column, repeatCount, parameters, callFrames, request)
+ {
+ super();
+
+ console.assert(typeof source === "string");
+ console.assert(typeof level === "string");
+ console.assert(typeof message === "string");
+ console.assert(target instanceof WebInspector.Target);
+ console.assert(!parameters || parameters.every((x) => x instanceof WebInspector.RemoteObject));
+
+ this._target = target;
+ this._source = source;
+ this._level = level;
+ this._messageText = message;
+ this._type = type || WebInspector.ConsoleMessage.MessageType.Log;
+
+ this._url = url || null;
+ this._line = line || 0;
+ this._column = column || 0;
+ this._sourceCodeLocation = undefined;
+
+ this._repeatCount = repeatCount || 0;
+ this._parameters = parameters;
+
+ callFrames = callFrames || [];
+ this._stackTrace = WebInspector.StackTrace.fromPayload(this._target, {callFrames});
+
+ this._request = request;
+ }
+
+ // Public
+
+ get target() { return this._target; }
+ get source() { return this._source; }
+ get level() { return this._level; }
+ get messageText() { return this._messageText; }
+ get type() { return this._type; }
+ get url() { return this._url; }
+ get line() { return this._line; }
+ get column() { return this._column; }
+ get repeatCount() { return this._repeatCount; }
+ get parameters() { return this._parameters; }
+ get stackTrace() { return this._stackTrace; }
+ get request() { return this._request; }
+
+ get sourceCodeLocation()
+ {
+ if (this._sourceCodeLocation !== undefined)
+ return this._sourceCodeLocation;
+
+ // First try to get the location from the top frame of the stack trace.
+ let topCallFrame = this._stackTrace.callFrames[0];
+ if (topCallFrame && topCallFrame.sourceCodeLocation) {
+ this._sourceCodeLocation = topCallFrame.sourceCodeLocation;
+ return this._sourceCodeLocation;
+ }
+
+ // If that doesn't exist try to get a location from the url/line/column in the ConsoleMessage.
+ // FIXME <http://webkit.org/b/76404>: Remove the string equality checks for undefined once we don't get that value anymore.
+ if (this._url && this._url !== "undefined") {
+ let sourceCode = WebInspector.frameResourceManager.resourceForURL(this._url);
+ if (sourceCode) {
+ let lineNumber = this._line > 0 ? this._line - 1 : 0;
+ let columnNumber = this._column > 0 ? this._column - 1 : 0;
+ this._sourceCodeLocation = new WebInspector.SourceCodeLocation(sourceCode, lineNumber, columnNumber);
+ return this._sourceCodeLocation;
+ }
+ }
+
+ this._sourceCodeLocation = null;
+ return this._sourceCodeLocation;
+ }
+};
+
+WebInspector.ConsoleMessage.MessageSource = {
+ HTML: "html",
+ XML: "xml",
+ JS: "javascript",
+ Network: "network",
+ ConsoleAPI: "console-api",
+ Storage: "storage",
+ Appcache: "appcache",
+ Rendering: "rendering",
+ CSS: "css",
+ Security: "security",
+ Other: "other",
+};
+
+WebInspector.ConsoleMessage.MessageType = {
+ Log: "log",
+ Dir: "dir",
+ DirXML: "dirxml",
+ Table: "table",
+ Trace: "trace",
+ StartGroup: "startGroup",
+ StartGroupCollapsed: "startGroupCollapsed",
+ EndGroup: "endGroup",
+ Assert: "assert",
+ Timing: "timing",
+ Profile: "profile",
+ ProfileEnd: "profileEnd",
+ Result: "result", // Frontend Only.
+};
+
+WebInspector.ConsoleMessage.MessageLevel = {
+ Log: "log",
+ Info: "info",
+ Warning: "warning",
+ Error: "error",
+ Debug: "debug",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ContentFlow.js b/Source/WebInspectorUI/UserInterface/Models/ContentFlow.js
new file mode 100644
index 000000000..cc063d3df
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ContentFlow.js
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+WebInspector.ContentFlow = class ContentFlow extends WebInspector.Object
+{
+ constructor(documentNodeIdentifier, name, overset, contentNodes)
+ {
+ super();
+
+ this._documentNodeIdentifier = documentNodeIdentifier;
+ this._name = name;
+ this._overset = overset;
+ this._contentNodes = contentNodes;
+ }
+
+ // Public
+
+ get id()
+ {
+ // Use the flow node id, to avoid collisions when we change main document id.
+ return this._documentNodeIdentifier + ":" + this._name;
+ }
+
+ get documentNodeIdentifier()
+ {
+ return this._documentNodeIdentifier;
+ }
+
+ get name()
+ {
+ return this._name;
+ }
+
+ get overset()
+ {
+ return this._overset;
+ }
+
+ set overset(overset)
+ {
+ if (this._overset === overset)
+ return;
+ this._overset = overset;
+ this.dispatchEventToListeners(WebInspector.ContentFlow.Event.FlowOversetWasChanged);
+ }
+
+ get contentNodes()
+ {
+ return this._contentNodes;
+ }
+
+ insertContentNodeBefore(contentNode, referenceNode)
+ {
+ var index = this._contentNodes.indexOf(referenceNode);
+ console.assert(index !== -1);
+ this._contentNodes.splice(index, 0, contentNode);
+ this.dispatchEventToListeners(WebInspector.ContentFlow.Event.ContentNodeWasAdded, {node: contentNode, before: referenceNode});
+ }
+
+ appendContentNode(contentNode)
+ {
+ this._contentNodes.push(contentNode);
+ this.dispatchEventToListeners(WebInspector.ContentFlow.Event.ContentNodeWasAdded, {node: contentNode});
+ }
+
+ removeContentNode(contentNode)
+ {
+ var index = this._contentNodes.indexOf(contentNode);
+ console.assert(index !== -1);
+ this._contentNodes.splice(index, 1);
+ this.dispatchEventToListeners(WebInspector.ContentFlow.Event.ContentNodeWasRemoved, {node: contentNode});
+ }
+};
+
+WebInspector.ContentFlow.Event = {
+ OversetWasChanged: "content-flow-overset-was-changed",
+ ContentNodeWasAdded: "content-flow-content-node-was-added",
+ ContentNodeWasRemoved: "content-flow-content-node-was-removed"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/CookieStorageObject.js b/Source/WebInspectorUI/UserInterface/Models/CookieStorageObject.js
new file mode 100644
index 000000000..fe41077be
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/CookieStorageObject.js
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CookieStorageObject = class CookieStorageObject
+{
+ constructor(host)
+ {
+ this._host = host;
+ }
+
+ // Static
+
+ static cookieMatchesResourceURL(cookie, resourceURL)
+ {
+ var parsedURL = parseURL(resourceURL);
+ if (!parsedURL || !WebInspector.CookieStorageObject.cookieDomainMatchesResourceDomain(cookie.domain, parsedURL.host))
+ return false;
+
+ return parsedURL.path.startsWith(cookie.path)
+ && (!cookie.port || parsedURL.port === cookie.port)
+ && (!cookie.secure || parsedURL.scheme === "https");
+ }
+
+ static cookieDomainMatchesResourceDomain(cookieDomain, resourceDomain)
+ {
+ if (cookieDomain.charAt(0) !== ".")
+ return resourceDomain === cookieDomain;
+ return !!resourceDomain.match(new RegExp("^(?:[^\\.]+\\.)*" + cookieDomain.substring(1).escapeForRegExp() + "$"), "i");
+ }
+
+ // Public
+
+ get host()
+ {
+ return this._host;
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ // FIXME <https://webkit.org/b/151413>: This class should actually store cookie data for this host.
+ cookie[WebInspector.CookieStorageObject.CookieHostCookieKey] = this.host;
+ }
+};
+
+WebInspector.CookieStorageObject.TypeIdentifier = "cookie-storage";
+WebInspector.CookieStorageObject.CookieHostCookieKey = "cookie-storage-host";
diff --git a/Source/WebInspectorUI/UserInterface/Models/DOMNode.js b/Source/WebInspectorUI/UserInterface/Models/DOMNode.js
new file mode 100644
index 000000000..636a7586f
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DOMNode.js
@@ -0,0 +1,833 @@
+/*
+ * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Joseph Pecoraro
+ * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DOMNode = class DOMNode extends WebInspector.Object
+{
+ constructor(domTreeManager, doc, isInShadowTree, payload)
+ {
+ super();
+
+ this._domTreeManager = domTreeManager;
+ this._isInShadowTree = isInShadowTree;
+
+ this.id = payload.nodeId;
+ this._domTreeManager._idToDOMNode[this.id] = this;
+
+ this._nodeType = payload.nodeType;
+ this._nodeName = payload.nodeName;
+ this._localName = payload.localName;
+ this._nodeValue = payload.nodeValue;
+ this._pseudoType = payload.pseudoType;
+ this._shadowRootType = payload.shadowRootType;
+ this._computedRole = payload.role;
+ this._contentSecurityPolicyHash = payload.contentSecurityPolicyHash;
+
+ if (this._nodeType === Node.DOCUMENT_NODE)
+ this.ownerDocument = this;
+ else
+ this.ownerDocument = doc;
+
+ this._attributes = [];
+ this._attributesMap = {};
+ if (payload.attributes)
+ this._setAttributesPayload(payload.attributes);
+
+ this._childNodeCount = payload.childNodeCount;
+ this._children = null;
+ this._filteredChildren = null;
+ this._filteredChildrenNeedsUpdating = true;
+
+ this._nextSibling = null;
+ this._previousSibling = null;
+ this.parentNode = null;
+
+ this._enabledPseudoClasses = [];
+
+ // FIXME: The logic around this._shadowRoots and this._children is very confusing.
+ this._shadowRoots = [];
+ if (payload.shadowRoots) {
+ for (var i = 0; i < payload.shadowRoots.length; ++i) {
+ var root = payload.shadowRoots[i];
+ var node = new WebInspector.DOMNode(this._domTreeManager, this.ownerDocument, true, root);
+ node.parentNode = this;
+ this._shadowRoots.push(node);
+ }
+ }
+
+ if (this._nodeType === Node.ELEMENT_NODE)
+ this._customElementState = payload.customElementState || WebInspector.DOMNode.CustomElementState.Builtin;
+ else
+ this._customElementState = null;
+
+ if (payload.templateContent) {
+ this._templateContent = new WebInspector.DOMNode(this._domTreeManager, this.ownerDocument, false, payload.templateContent);
+ this._templateContent.parentNode = this;
+ }
+
+ if (payload.children)
+ this._setChildrenPayload(payload.children);
+ else if (!this._children && this._shadowRoots.length)
+ this._children = this._shadowRoots.slice();
+
+ this._pseudoElements = new Map;
+ if (payload.pseudoElements) {
+ for (var i = 0; i < payload.pseudoElements.length; ++i) {
+ var node = new WebInspector.DOMNode(this._domTreeManager, this.ownerDocument, this._isInShadowTree, payload.pseudoElements[i]);
+ node.parentNode = this;
+ this._pseudoElements.set(node.pseudoType(), node);
+ }
+ }
+
+ if (payload.contentDocument) {
+ this._contentDocument = new WebInspector.DOMNode(this._domTreeManager, null, false, payload.contentDocument);
+ this._children = [this._contentDocument];
+ this._renumber();
+ }
+
+ if (payload.frameId)
+ this._frameIdentifier = payload.frameId;
+
+ if (this._nodeType === Node.ELEMENT_NODE) {
+ // HTML and BODY from internal iframes should not overwrite top-level ones.
+ if (this.ownerDocument && !this.ownerDocument.documentElement && this._nodeName === "HTML")
+ this.ownerDocument.documentElement = this;
+ if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY")
+ this.ownerDocument.body = this;
+ if (payload.documentURL)
+ this.documentURL = payload.documentURL;
+ } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
+ this.publicId = payload.publicId;
+ this.systemId = payload.systemId;
+ } else if (this._nodeType === Node.DOCUMENT_NODE) {
+ this.documentURL = payload.documentURL;
+ this.xmlVersion = payload.xmlVersion;
+ } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
+ this.name = payload.name;
+ this.value = payload.value;
+ }
+ }
+
+ // Public
+
+ get frameIdentifier()
+ {
+ return this._frameIdentifier || this.ownerDocument.frameIdentifier;
+ }
+
+ get frame()
+ {
+ if (!this._frame)
+ this._frame = WebInspector.frameResourceManager.frameForIdentifier(this.frameIdentifier);
+ return this._frame;
+ }
+
+ get children()
+ {
+ if (!this._children)
+ return null;
+
+ if (WebInspector.showShadowDOMSetting.value)
+ return this._children;
+
+ if (this._filteredChildrenNeedsUpdating) {
+ this._filteredChildrenNeedsUpdating = false;
+ this._filteredChildren = this._children.filter(function(node) {
+ return !node._isInShadowTree;
+ });
+ }
+
+ return this._filteredChildren;
+ }
+
+ get firstChild()
+ {
+ var children = this.children;
+
+ if (children && children.length > 0)
+ return children[0];
+
+ return null;
+ }
+
+ get lastChild()
+ {
+ var children = this.children;
+
+ if (children && children.length > 0)
+ return children.lastValue;
+
+ return null;
+ }
+
+ get nextSibling()
+ {
+ if (WebInspector.showShadowDOMSetting.value)
+ return this._nextSibling;
+
+ var node = this._nextSibling;
+ while (node) {
+ if (!node._isInShadowTree)
+ return node;
+ node = node._nextSibling;
+ }
+ return null;
+ }
+
+ get previousSibling()
+ {
+ if (WebInspector.showShadowDOMSetting.value)
+ return this._previousSibling;
+
+ var node = this._previousSibling;
+ while (node) {
+ if (!node._isInShadowTree)
+ return node;
+ node = node._previousSibling;
+ }
+ return null;
+ }
+
+ get childNodeCount()
+ {
+ var children = this.children;
+ if (children)
+ return children.length;
+
+ if (WebInspector.showShadowDOMSetting.value)
+ return this._childNodeCount + this._shadowRoots.length;
+
+ return this._childNodeCount;
+ }
+
+ set childNodeCount(count)
+ {
+ this._childNodeCount = count;
+ }
+
+ computedRole()
+ {
+ return this._computedRole;
+ }
+
+ contentSecurityPolicyHash()
+ {
+ return this._contentSecurityPolicyHash;
+ }
+
+ hasAttributes()
+ {
+ return this._attributes.length > 0;
+ }
+
+ hasChildNodes()
+ {
+ return this.childNodeCount > 0;
+ }
+
+ hasShadowRoots()
+ {
+ return !!this._shadowRoots.length;
+ }
+
+ isInShadowTree()
+ {
+ return this._isInShadowTree;
+ }
+
+ isInUserAgentShadowTree()
+ {
+ return this._isInShadowTree && this.ancestorShadowRoot().isUserAgentShadowRoot();
+ }
+
+ isCustomElement()
+ {
+ return this._customElementState === WebInspector.DOMNode.CustomElementState.Custom;
+ }
+
+ customElementState()
+ {
+ return this._customElementState;
+ }
+
+ isShadowRoot()
+ {
+ return !!this._shadowRootType;
+ }
+
+ isUserAgentShadowRoot()
+ {
+ return this._shadowRootType === WebInspector.DOMNode.ShadowRootType.UserAgent;
+ }
+
+ ancestorShadowRoot()
+ {
+ if (!this._isInShadowTree)
+ return null;
+
+ let node = this;
+ while (node && !node.isShadowRoot())
+ node = node.parentNode;
+ return node;
+ }
+
+ ancestorShadowHost()
+ {
+ let shadowRoot = this.ancestorShadowRoot();
+ return shadowRoot ? shadowRoot.parentNode : null;
+ }
+
+ isPseudoElement()
+ {
+ return this._pseudoType !== undefined;
+ }
+
+ nodeType()
+ {
+ return this._nodeType;
+ }
+
+ nodeName()
+ {
+ return this._nodeName;
+ }
+
+ nodeNameInCorrectCase()
+ {
+ return this.isXMLNode() ? this.nodeName() : this.nodeName().toLowerCase();
+ }
+
+ setNodeName(name, callback)
+ {
+ DOMAgent.setNodeName(this.id, name, this._makeUndoableCallback(callback));
+ }
+
+ localName()
+ {
+ return this._localName;
+ }
+
+ templateContent()
+ {
+ return this._templateContent || null;
+ }
+
+ pseudoType()
+ {
+ return this._pseudoType;
+ }
+
+ hasPseudoElements()
+ {
+ return this._pseudoElements.size > 0;
+ }
+
+ pseudoElements()
+ {
+ return this._pseudoElements;
+ }
+
+ beforePseudoElement()
+ {
+ return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementType.Before) || null;
+ }
+
+ afterPseudoElement()
+ {
+ return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementType.After) || null;
+ }
+
+ shadowRoots()
+ {
+ return this._shadowRoots;
+ }
+
+ shadowRootType()
+ {
+ return this._shadowRootType;
+ }
+
+ nodeValue()
+ {
+ return this._nodeValue;
+ }
+
+ setNodeValue(value, callback)
+ {
+ DOMAgent.setNodeValue(this.id, value, this._makeUndoableCallback(callback));
+ }
+
+ getAttribute(name)
+ {
+ var attr = this._attributesMap[name];
+ return attr ? attr.value : undefined;
+ }
+
+ setAttribute(name, text, callback)
+ {
+ DOMAgent.setAttributesAsText(this.id, text, name, this._makeUndoableCallback(callback));
+ }
+
+ setAttributeValue(name, value, callback)
+ {
+ DOMAgent.setAttributeValue(this.id, name, value, this._makeUndoableCallback(callback));
+ }
+
+ attributes()
+ {
+ return this._attributes;
+ }
+
+ removeAttribute(name, callback)
+ {
+ function mycallback(error, success)
+ {
+ if (!error) {
+ delete this._attributesMap[name];
+ for (var i = 0; i < this._attributes.length; ++i) {
+ if (this._attributes[i].name === name) {
+ this._attributes.splice(i, 1);
+ break;
+ }
+ }
+ }
+
+ this._makeUndoableCallback(callback)(error);
+ }
+ DOMAgent.removeAttribute(this.id, name, mycallback.bind(this));
+ }
+
+ toggleClass(className, flag)
+ {
+ if (!className || !className.length)
+ return;
+
+ if (this.isPseudoElement()) {
+ this.parentNode.toggleClass(className, flag);
+ return;
+ }
+
+ if (this.nodeType() !== Node.ELEMENT_NODE)
+ return;
+
+ function resolvedNode(object)
+ {
+ if (!object)
+ return;
+
+ function inspectedPage_node_toggleClass(className, flag)
+ {
+ this.classList.toggle(className, flag);
+ }
+
+ object.callFunction(inspectedPage_node_toggleClass, [className, flag]);
+ object.release();
+ }
+
+ WebInspector.RemoteObject.resolveNode(this, "", resolvedNode);
+ }
+
+ getChildNodes(callback)
+ {
+ if (this.children) {
+ if (callback)
+ callback(this.children);
+ return;
+ }
+
+ function mycallback(error) {
+ if (!error && callback)
+ callback(this.children);
+ }
+
+ DOMAgent.requestChildNodes(this.id, mycallback.bind(this));
+ }
+
+ getSubtree(depth, callback)
+ {
+ function mycallback(error)
+ {
+ if (callback)
+ callback(error ? null : this.children);
+ }
+
+ DOMAgent.requestChildNodes(this.id, depth, mycallback.bind(this));
+ }
+
+ getOuterHTML(callback)
+ {
+ DOMAgent.getOuterHTML(this.id, callback);
+ }
+
+ setOuterHTML(html, callback)
+ {
+ DOMAgent.setOuterHTML(this.id, html, this._makeUndoableCallback(callback));
+ }
+
+ removeNode(callback)
+ {
+ DOMAgent.removeNode(this.id, this._makeUndoableCallback(callback));
+ }
+
+ copyNode()
+ {
+ function copy(error, text)
+ {
+ if (!error)
+ InspectorFrontendHost.copyText(text);
+ }
+ DOMAgent.getOuterHTML(this.id, copy);
+ }
+
+ eventListeners(callback)
+ {
+ DOMAgent.getEventListenersForNode(this.id, callback);
+ }
+
+ accessibilityProperties(callback)
+ {
+ function accessibilityPropertiesCallback(error, accessibilityProperties)
+ {
+ if (!error && callback && accessibilityProperties) {
+ callback({
+ activeDescendantNodeId: accessibilityProperties.activeDescendantNodeId,
+ busy: accessibilityProperties.busy,
+ checked: accessibilityProperties.checked,
+ childNodeIds: accessibilityProperties.childNodeIds,
+ controlledNodeIds: accessibilityProperties.controlledNodeIds,
+ current: accessibilityProperties.current,
+ disabled: accessibilityProperties.disabled,
+ exists: accessibilityProperties.exists,
+ expanded: accessibilityProperties.expanded,
+ flowedNodeIds: accessibilityProperties.flowedNodeIds,
+ focused: accessibilityProperties.focused,
+ ignored: accessibilityProperties.ignored,
+ ignoredByDefault: accessibilityProperties.ignoredByDefault,
+ invalid: accessibilityProperties.invalid,
+ isPopupButton: accessibilityProperties.isPopUpButton,
+ headingLevel: accessibilityProperties.headingLevel,
+ hierarchyLevel: accessibilityProperties.hierarchyLevel,
+ hidden: accessibilityProperties.hidden,
+ label: accessibilityProperties.label,
+ liveRegionAtomic: accessibilityProperties.liveRegionAtomic,
+ liveRegionRelevant: accessibilityProperties.liveRegionRelevant,
+ liveRegionStatus: accessibilityProperties.liveRegionStatus,
+ mouseEventNodeId: accessibilityProperties.mouseEventNodeId,
+ nodeId: accessibilityProperties.nodeId,
+ ownedNodeIds: accessibilityProperties.ownedNodeIds,
+ parentNodeId: accessibilityProperties.parentNodeId,
+ pressed: accessibilityProperties.pressed,
+ readonly: accessibilityProperties.readonly,
+ required: accessibilityProperties.required,
+ role: accessibilityProperties.role,
+ selected: accessibilityProperties.selected,
+ selectedChildNodeIds: accessibilityProperties.selectedChildNodeIds
+ });
+ }
+ }
+ DOMAgent.getAccessibilityPropertiesForNode(this.id, accessibilityPropertiesCallback.bind(this));
+ }
+
+ path()
+ {
+ var path = [];
+ var node = this;
+ while (node && "index" in node && node._nodeName.length) {
+ path.push([node.index, node._nodeName]);
+ node = node.parentNode;
+ }
+ path.reverse();
+ return path.join(",");
+ }
+
+ get escapedIdSelector()
+ {
+ let id = this.getAttribute("id");
+ if (!id)
+ return "";
+
+ id = id.trim();
+ if (!id.length)
+ return "";
+
+ id = CSS.escape(id);
+ if (/[\s'"]/.test(id))
+ return `[id=\"${id}\"]`;
+
+ return `#${id}`;
+ }
+
+ get escapedClassSelector()
+ {
+ let classes = this.getAttribute("class");
+ if (!classes)
+ return "";
+
+ classes = classes.trim();
+ if (!classes.length)
+ return "";
+
+ let foundClasses = new Set;
+ return classes.split(/\s+/).reduce((selector, className) => {
+ if (!className.length || foundClasses.has(className))
+ return selector;
+
+ foundClasses.add(className);
+ return `${selector}.${CSS.escape(className)}`;
+ }, "");
+ }
+
+ get displayName()
+ {
+ return this.nodeNameInCorrectCase() + this.escapedIdSelector + this.escapedClassSelector;
+ }
+
+ appropriateSelectorFor(justSelector)
+ {
+ if (this.isPseudoElement())
+ return this.parentNode.appropriateSelectorFor() + "::" + this._pseudoType;
+
+ let lowerCaseName = this.localName() || this.nodeName().toLowerCase();
+
+ let id = this.escapedIdSelector;
+ if (id.length)
+ return justSelector ? id : lowerCaseName + id;
+
+ let classes = this.escapedClassSelector;
+ if (classes.length)
+ return justSelector ? classes : lowerCaseName + classes;
+
+ if (lowerCaseName === "input" && this.getAttribute("type"))
+ return lowerCaseName + "[type=\"" + this.getAttribute("type") + "\"]";
+
+ return lowerCaseName;
+ }
+
+ isAncestor(node)
+ {
+ if (!node)
+ return false;
+
+ var currentNode = node.parentNode;
+ while (currentNode) {
+ if (this === currentNode)
+ return true;
+ currentNode = currentNode.parentNode;
+ }
+ return false;
+ }
+
+ isDescendant(descendant)
+ {
+ return descendant !== null && descendant.isAncestor(this);
+ }
+
+ get ownerSVGElement()
+ {
+ if (this._nodeName === "svg")
+ return this;
+
+ if (!this.parentNode)
+ return null;
+
+ return this.parentNode.ownerSVGElement;
+ }
+
+ isSVGElement()
+ {
+ return !!this.ownerSVGElement;
+ }
+
+ _setAttributesPayload(attrs)
+ {
+ this._attributes = [];
+ this._attributesMap = {};
+ for (var i = 0; i < attrs.length; i += 2)
+ this._addAttribute(attrs[i], attrs[i + 1]);
+ }
+
+ _insertChild(prev, payload)
+ {
+ var node = new WebInspector.DOMNode(this._domTreeManager, this.ownerDocument, this._isInShadowTree, payload);
+ if (!prev) {
+ if (!this._children) {
+ // First node
+ this._children = this._shadowRoots.concat([node]);
+ } else
+ this._children.unshift(node);
+ } else
+ this._children.splice(this._children.indexOf(prev) + 1, 0, node);
+ this._renumber();
+ return node;
+ }
+
+ _removeChild(node)
+ {
+ // FIXME: Handle removal if this is a shadow root.
+ if (node.isPseudoElement()) {
+ this._pseudoElements.delete(node.pseudoType());
+ node.parentNode = null;
+ } else {
+ this._children.splice(this._children.indexOf(node), 1);
+ node.parentNode = null;
+ this._renumber();
+ }
+ }
+
+ _setChildrenPayload(payloads)
+ {
+ // We set children in the constructor.
+ if (this._contentDocument)
+ return;
+
+ this._children = this._shadowRoots.slice();
+ for (var i = 0; i < payloads.length; ++i) {
+ var node = new WebInspector.DOMNode(this._domTreeManager, this.ownerDocument, this._isInShadowTree, payloads[i]);
+ this._children.push(node);
+ }
+ this._renumber();
+ }
+
+ _renumber()
+ {
+ this._filteredChildrenNeedsUpdating = true;
+
+ var childNodeCount = this._children.length;
+ if (childNodeCount === 0)
+ return;
+
+ for (var i = 0; i < childNodeCount; ++i) {
+ var child = this._children[i];
+ child.index = i;
+ child._nextSibling = i + 1 < childNodeCount ? this._children[i + 1] : null;
+ child._previousSibling = i - 1 >= 0 ? this._children[i - 1] : null;
+ child.parentNode = this;
+ }
+ }
+
+ _addAttribute(name, value)
+ {
+ var attr = {name, value, _node: this};
+ this._attributesMap[name] = attr;
+ this._attributes.push(attr);
+ }
+
+ _setAttribute(name, value)
+ {
+ var attr = this._attributesMap[name];
+ if (attr)
+ attr.value = value;
+ else
+ this._addAttribute(name, value);
+ }
+
+ _removeAttribute(name)
+ {
+ var attr = this._attributesMap[name];
+ if (attr) {
+ this._attributes.remove(attr);
+ delete this._attributesMap[name];
+ }
+ }
+
+ moveTo(targetNode, anchorNode, callback)
+ {
+ DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, this._makeUndoableCallback(callback));
+ }
+
+ isXMLNode()
+ {
+ return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
+ }
+
+ get enabledPseudoClasses()
+ {
+ return this._enabledPseudoClasses;
+ }
+
+ setPseudoClassEnabled(pseudoClass, enabled)
+ {
+ var pseudoClasses = this._enabledPseudoClasses;
+ if (enabled) {
+ if (pseudoClasses.includes(pseudoClass))
+ return;
+ pseudoClasses.push(pseudoClass);
+ } else {
+ if (!pseudoClasses.includes(pseudoClass))
+ return;
+ pseudoClasses.remove(pseudoClass);
+ }
+
+ function changed(error)
+ {
+ if (!error)
+ this.dispatchEventToListeners(WebInspector.DOMNode.Event.EnabledPseudoClassesChanged);
+ }
+
+ CSSAgent.forcePseudoState(this.id, pseudoClasses, changed.bind(this));
+ }
+
+ _makeUndoableCallback(callback)
+ {
+ return function(error)
+ {
+ if (!error)
+ DOMAgent.markUndoableState();
+
+ if (callback)
+ callback.apply(null, arguments);
+ };
+ }
+};
+
+WebInspector.DOMNode.Event = {
+ EnabledPseudoClassesChanged: "dom-node-enabled-pseudo-classes-did-change",
+ AttributeModified: "dom-node-attribute-modified",
+ AttributeRemoved: "dom-node-attribute-removed"
+};
+
+WebInspector.DOMNode.PseudoElementType = {
+ Before: "before",
+ After: "after",
+};
+
+WebInspector.DOMNode.ShadowRootType = {
+ UserAgent: "user-agent",
+ Closed: "closed",
+ Open: "open",
+};
+
+WebInspector.DOMNode.CustomElementState = {
+ Builtin: "builtin",
+ Custom: "custom",
+ Waiting: "waiting",
+ Failed: "failed",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js b/Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js
new file mode 100644
index 000000000..564be5e52
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js
@@ -0,0 +1,985 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DOMNodeStyles = class DOMNodeStyles extends WebInspector.Object
+{
+ constructor(node)
+ {
+ super();
+
+ console.assert(node);
+ this._node = node || null;
+
+ this._rulesMap = {};
+ this._styleDeclarationsMap = {};
+
+ this._matchedRules = [];
+ this._inheritedRules = [];
+ this._pseudoElements = {};
+ this._inlineStyle = null;
+ this._attributesStyle = null;
+ this._computedStyle = null;
+ this._orderedStyles = [];
+
+ this._propertyNameToEffectivePropertyMap = {};
+
+ this._pendingRefreshTask = null;
+ this.refresh();
+ }
+
+ // Public
+
+ get node()
+ {
+ return this._node;
+ }
+
+ get needsRefresh()
+ {
+ return this._pendingRefreshTask || this._needsRefresh;
+ }
+
+ refreshIfNeeded()
+ {
+ if (!this._needsRefresh)
+ return;
+ this.refresh();
+ }
+
+ refresh()
+ {
+ if (this._pendingRefreshTask)
+ return this._pendingRefreshTask;
+
+ this._needsRefresh = false;
+
+ let fetchedMatchedStylesPromise = new WebInspector.WrappedPromise;
+ let fetchedInlineStylesPromise = new WebInspector.WrappedPromise;
+ let fetchedComputedStylesPromise = new WebInspector.WrappedPromise;
+
+ function parseRuleMatchArrayPayload(matchArray, node, inherited)
+ {
+ var result = [];
+
+ // Iterate in reverse order to match the cascade order.
+ var ruleOccurrences = {};
+ for (var i = matchArray.length - 1; i >= 0; --i) {
+ var rule = this._parseRulePayload(matchArray[i].rule, matchArray[i].matchingSelectors, node, inherited, ruleOccurrences);
+ if (!rule)
+ continue;
+ result.push(rule);
+ }
+
+ return result;
+ }
+
+ function fetchedMatchedStyles(error, matchedRulesPayload, pseudoElementRulesPayload, inheritedRulesPayload)
+ {
+ matchedRulesPayload = matchedRulesPayload || [];
+ pseudoElementRulesPayload = pseudoElementRulesPayload || [];
+ inheritedRulesPayload = inheritedRulesPayload || [];
+
+ // Move the current maps to previous.
+ this._previousRulesMap = this._rulesMap;
+ this._previousStyleDeclarationsMap = this._styleDeclarationsMap;
+
+ // Clear the current maps.
+ this._rulesMap = {};
+ this._styleDeclarationsMap = {};
+
+ this._matchedRules = parseRuleMatchArrayPayload.call(this, matchedRulesPayload, this._node);
+
+ this._pseudoElements = {};
+ for (var pseudoElementRulePayload of pseudoElementRulesPayload) {
+ var pseudoElementRules = parseRuleMatchArrayPayload.call(this, pseudoElementRulePayload.matches, this._node);
+ this._pseudoElements[pseudoElementRulePayload.pseudoId] = {matchedRules: pseudoElementRules};
+ }
+
+ this._inheritedRules = [];
+
+ var i = 0;
+ var currentNode = this._node.parentNode;
+ while (currentNode && i < inheritedRulesPayload.length) {
+ var inheritedRulePayload = inheritedRulesPayload[i];
+
+ var inheritedRuleInfo = {node: currentNode};
+ inheritedRuleInfo.inlineStyle = inheritedRulePayload.inlineStyle ? this._parseStyleDeclarationPayload(inheritedRulePayload.inlineStyle, currentNode, true, WebInspector.CSSStyleDeclaration.Type.Inline) : null;
+ inheritedRuleInfo.matchedRules = inheritedRulePayload.matchedCSSRules ? parseRuleMatchArrayPayload.call(this, inheritedRulePayload.matchedCSSRules, currentNode, true) : [];
+
+ if (inheritedRuleInfo.inlineStyle || inheritedRuleInfo.matchedRules.length)
+ this._inheritedRules.push(inheritedRuleInfo);
+
+ currentNode = currentNode.parentNode;
+ ++i;
+ }
+
+ fetchedMatchedStylesPromise.resolve();
+ }
+
+ function fetchedInlineStyles(error, inlineStylePayload, attributesStylePayload)
+ {
+ this._inlineStyle = inlineStylePayload ? this._parseStyleDeclarationPayload(inlineStylePayload, this._node, false, WebInspector.CSSStyleDeclaration.Type.Inline) : null;
+ this._attributesStyle = attributesStylePayload ? this._parseStyleDeclarationPayload(attributesStylePayload, this._node, false, WebInspector.CSSStyleDeclaration.Type.Attribute) : null;
+
+ this._updateStyleCascade();
+
+ fetchedInlineStylesPromise.resolve();
+ }
+
+ function fetchedComputedStyle(error, computedPropertiesPayload)
+ {
+ var properties = [];
+ for (var i = 0; computedPropertiesPayload && i < computedPropertiesPayload.length; ++i) {
+ var propertyPayload = computedPropertiesPayload[i];
+
+ var canonicalName = WebInspector.cssStyleManager.canonicalNameForPropertyName(propertyPayload.name);
+ propertyPayload.implicit = !this._propertyNameToEffectivePropertyMap[canonicalName];
+
+ var property = this._parseStylePropertyPayload(propertyPayload, NaN, this._computedStyle);
+ if (!property.implicit)
+ property.implicit = !this._isPropertyFoundInMatchingRules(property.name);
+ properties.push(property);
+ }
+
+ if (this._computedStyle)
+ this._computedStyle.update(null, properties);
+ else
+ this._computedStyle = new WebInspector.CSSStyleDeclaration(this, null, null, WebInspector.CSSStyleDeclaration.Type.Computed, this._node, false, null, properties);
+
+ let significantChange = false;
+ for (let key in this._styleDeclarationsMap) {
+ // Check if the same key exists in the previous map and has the same style objects.
+ if (key in this._previousStyleDeclarationsMap) {
+ if (Array.shallowEqual(this._styleDeclarationsMap[key], this._previousStyleDeclarationsMap[key]))
+ continue;
+
+ // Some styles have selectors such that they will match with the DOM node twice (for example "::before, ::after").
+ // In this case a second style for a second matching may be generated and added which will cause the shallowEqual
+ // to not return true, so in this case we just want to ensure that all the current styles existed previously.
+ let styleFound = false;
+ for (let style of this._styleDeclarationsMap[key]) {
+ if (this._previousStyleDeclarationsMap[key].includes(style)) {
+ styleFound = true;
+ break;
+ }
+ }
+
+ if (styleFound)
+ continue;
+ }
+
+ if (!this._includeUserAgentRulesOnNextRefresh) {
+ // We can assume all the styles with the same key are from the same stylesheet and rule, so we only check the first.
+ let firstStyle = this._styleDeclarationsMap[key][0];
+ if (firstStyle && firstStyle.ownerRule && firstStyle.ownerRule.type === WebInspector.CSSStyleSheet.Type.UserAgent) {
+ // User Agent styles get different identifiers after some edits. This would cause us to fire a significant refreshed
+ // event more than it is helpful. And since the user agent stylesheet is static it shouldn't match differently
+ // between refreshes for the same node. This issue is tracked by: https://webkit.org/b/110055
+ continue;
+ }
+ }
+
+ // This key is new or has different style objects than before. This is a significant change.
+ significantChange = true;
+ break;
+ }
+
+ if (!significantChange) {
+ for (var key in this._previousStyleDeclarationsMap) {
+ // Check if the same key exists in current map. If it does exist it was already checked for equality above.
+ if (key in this._styleDeclarationsMap)
+ continue;
+
+ if (!this._includeUserAgentRulesOnNextRefresh) {
+ // See above for why we skip user agent style rules.
+ var firstStyle = this._previousStyleDeclarationsMap[key][0];
+ if (firstStyle && firstStyle.ownerRule && firstStyle.ownerRule.type === WebInspector.CSSStyleSheet.Type.UserAgent)
+ continue;
+ }
+
+ // This key no longer exists. This is a significant change.
+ significantChange = true;
+ break;
+ }
+ }
+
+ delete this._includeUserAgentRulesOnNextRefresh;
+
+ // Delete the previous maps now that any reused rules and style have been moved over.
+ delete this._previousRulesMap;
+ delete this._previousStyleDeclarationsMap;
+
+ this.dispatchEventToListeners(WebInspector.DOMNodeStyles.Event.Refreshed, {significantChange});
+
+ fetchedComputedStylesPromise.resolve();
+ }
+
+ // FIXME: Convert to pushing StyleSheet information to the frontend. <rdar://problem/13213680>
+ WebInspector.cssStyleManager.fetchStyleSheetsIfNeeded();
+
+ CSSAgent.getMatchedStylesForNode.invoke({nodeId: this._node.id, includePseudo: true, includeInherited: true}, fetchedMatchedStyles.bind(this));
+ CSSAgent.getInlineStylesForNode.invoke({nodeId: this._node.id}, fetchedInlineStyles.bind(this));
+ CSSAgent.getComputedStyleForNode.invoke({nodeId: this._node.id}, fetchedComputedStyle.bind(this));
+
+ this._pendingRefreshTask = Promise.all([fetchedMatchedStylesPromise.promise, fetchedInlineStylesPromise.promise, fetchedComputedStylesPromise.promise])
+ .then(() => {
+ this._pendingRefreshTask = null;
+ });
+
+ return this._pendingRefreshTask;
+ }
+
+ addRule(selector, text)
+ {
+ selector = selector || this._node.appropriateSelectorFor(true);
+
+ function completed()
+ {
+ DOMAgent.markUndoableState();
+ this.refresh();
+ }
+
+ function styleChanged(error, stylePayload)
+ {
+ if (error)
+ return;
+
+ completed.call(this);
+ }
+
+ function addedRule(error, rulePayload)
+ {
+ if (error)
+ return;
+
+ if (!text || !text.length) {
+ completed.call(this);
+ return;
+ }
+
+ CSSAgent.setStyleText(rulePayload.style.styleId, text, styleChanged.bind(this));
+ }
+
+ // COMPATIBILITY (iOS 9): Before CSS.createStyleSheet, CSS.addRule could be called with a contextNode.
+ if (!CSSAgent.createStyleSheet) {
+ CSSAgent.addRule.invoke({contextNodeId: this._node.id, selector}, addedRule.bind(this));
+ return;
+ }
+
+ function inspectorStyleSheetAvailable(styleSheet)
+ {
+ CSSAgent.addRule(styleSheet.id, selector, addedRule.bind(this));
+ }
+
+ WebInspector.cssStyleManager.preferredInspectorStyleSheetForFrame(this._node.frame, inspectorStyleSheetAvailable.bind(this));
+ }
+
+ rulesForSelector(selector)
+ {
+ selector = selector || this._node.appropriateSelectorFor(true);
+
+ function ruleHasSelector(rule) {
+ return !rule.mediaList.length && rule.selectorText === selector;
+ }
+
+ let rules = this._matchedRules.filter(ruleHasSelector);
+
+ for (let id in this._pseudoElements)
+ rules = rules.concat(this._pseudoElements[id].matchedRules.filter(ruleHasSelector));
+
+ return rules;
+ }
+
+ get matchedRules()
+ {
+ return this._matchedRules;
+ }
+
+ get inheritedRules()
+ {
+ return this._inheritedRules;
+ }
+
+ get inlineStyle()
+ {
+ return this._inlineStyle;
+ }
+
+ get attributesStyle()
+ {
+ return this._attributesStyle;
+ }
+
+ get pseudoElements()
+ {
+ return this._pseudoElements;
+ }
+
+ get computedStyle()
+ {
+ return this._computedStyle;
+ }
+
+ get orderedStyles()
+ {
+ return this._orderedStyles;
+ }
+
+ effectivePropertyForName(name)
+ {
+ let property = this._propertyNameToEffectivePropertyMap[name];
+ if (property)
+ return property;
+
+ let canonicalName = WebInspector.cssStyleManager.canonicalNameForPropertyName(name);
+ return this._propertyNameToEffectivePropertyMap[canonicalName] || null;
+ }
+
+ // Protected
+
+ mediaQueryResultDidChange()
+ {
+ this._markAsNeedsRefresh();
+ }
+
+ pseudoClassesDidChange(node)
+ {
+ this._includeUserAgentRulesOnNextRefresh = true;
+ this._markAsNeedsRefresh();
+ }
+
+ attributeDidChange(node, attributeName)
+ {
+ this._markAsNeedsRefresh();
+ }
+
+ changeRule(rule, selector, text)
+ {
+ if (!rule)
+ return;
+
+ selector = selector || "";
+
+ function changeCompleted()
+ {
+ DOMAgent.markUndoableState();
+ this.refresh();
+ }
+
+ function styleChanged(error, stylePayload)
+ {
+ if (error)
+ return;
+
+ changeCompleted.call(this);
+ }
+
+ function changeText(styleId)
+ {
+ if (!text || !text.length) {
+ changeCompleted.call(this);
+ return;
+ }
+
+ CSSAgent.setStyleText(styleId, text, styleChanged.bind(this));
+ }
+
+ function ruleSelectorChanged(error, rulePayload)
+ {
+ if (error)
+ return;
+
+ changeText.call(this, rulePayload.style.styleId);
+ }
+
+ this._needsRefresh = true;
+ this._ignoreNextContentDidChangeForStyleSheet = rule.ownerStyleSheet;
+
+ CSSAgent.setRuleSelector(rule.id, selector, ruleSelectorChanged.bind(this));
+ }
+
+ changeRuleSelector(rule, selector)
+ {
+ selector = selector || "";
+ let result = new WebInspector.WrappedPromise;
+
+ function ruleSelectorChanged(error, rulePayload)
+ {
+ if (error) {
+ result.reject(error);
+ return;
+ }
+
+ DOMAgent.markUndoableState();
+
+ // Do a full refresh incase the rule no longer matches the node or the
+ // matched selector indices changed.
+ this.refresh().then(() => {
+ result.resolve(rulePayload);
+ });
+ }
+
+ this._needsRefresh = true;
+ this._ignoreNextContentDidChangeForStyleSheet = rule.ownerStyleSheet;
+
+ CSSAgent.setRuleSelector(rule.id, selector, ruleSelectorChanged.bind(this));
+ return result.promise;
+ }
+
+ changeStyleText(style, text)
+ {
+ if (!style.ownerStyleSheet || !style.styleSheetTextRange)
+ return;
+
+ text = text || "";
+
+ function styleChanged(error, stylePayload)
+ {
+ if (error)
+ return;
+ this.refresh();
+ }
+
+ CSSAgent.setStyleText(style.id, text, styleChanged.bind(this));
+ }
+
+ // Private
+
+ _createSourceCodeLocation(sourceURL, sourceLine, sourceColumn)
+ {
+ if (!sourceURL)
+ return null;
+
+ var sourceCode;
+
+ // Try to use the node to find the frame which has the correct resource first.
+ if (this._node.ownerDocument) {
+ var mainResource = WebInspector.frameResourceManager.resourceForURL(this._node.ownerDocument.documentURL);
+ if (mainResource) {
+ var parentFrame = mainResource.parentFrame;
+ sourceCode = parentFrame.resourceForURL(sourceURL);
+ }
+ }
+
+ // If that didn't find the resource, then search all frames.
+ if (!sourceCode)
+ sourceCode = WebInspector.frameResourceManager.resourceForURL(sourceURL);
+
+ if (!sourceCode)
+ return null;
+
+ return sourceCode.createSourceCodeLocation(sourceLine || 0, sourceColumn || 0);
+ }
+
+ _parseSourceRangePayload(payload)
+ {
+ if (!payload)
+ return null;
+
+ return new WebInspector.TextRange(payload.startLine, payload.startColumn, payload.endLine, payload.endColumn);
+ }
+
+ _parseStylePropertyPayload(payload, index, styleDeclaration, styleText)
+ {
+ var text = payload.text || "";
+ var name = payload.name;
+ var value = (payload.value || "").replace(/\s*!important\s*$/, "");
+ var priority = payload.priority || "";
+
+ var enabled = true;
+ var overridden = false;
+ var implicit = payload.implicit || false;
+ var anonymous = false;
+ var valid = "parsedOk" in payload ? payload.parsedOk : true;
+
+ switch (payload.status || "style") {
+ case "active":
+ enabled = true;
+ break;
+ case "inactive":
+ overridden = true;
+ enabled = true;
+ break;
+ case "disabled":
+ enabled = false;
+ break;
+ case "style":
+ // FIXME: Is this still needed? This includes UserAgent styles and HTML attribute styles.
+ anonymous = true;
+ break;
+ }
+
+ var styleSheetTextRange = this._parseSourceRangePayload(payload.range);
+
+ if (styleDeclaration) {
+ // Use propertyForName when the index is NaN since propertyForName is fast in that case.
+ var property = isNaN(index) ? styleDeclaration.propertyForName(name, true) : styleDeclaration.properties[index];
+
+ // Reuse a property if the index and name matches. Otherwise it is a different property
+ // and should be created from scratch. This works in the simple cases where only existing
+ // properties change in place and no properties are inserted or deleted at the beginning.
+ // FIXME: This could be smarter by ignoring index and just go by name. However, that gets
+ // tricky for rules that have more than one property with the same name.
+ if (property && property.name === name && (property.index === index || (isNaN(property.index) && isNaN(index)))) {
+ property.update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange);
+ return property;
+ }
+
+ // Reuse a pending property with the same name. These properties are pending being committed,
+ // so if we find a match that likely means it got committed and we should use it.
+ var pendingProperties = styleDeclaration.pendingProperties;
+ for (var i = 0; i < pendingProperties.length; ++i) {
+ var pendingProperty = pendingProperties[i];
+ if (pendingProperty.name === name && isNaN(pendingProperty.index)) {
+ pendingProperty.index = index;
+ pendingProperty.update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange);
+ return pendingProperty;
+ }
+ }
+ }
+
+ return new WebInspector.CSSProperty(index, text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange);
+ }
+
+ _parseStyleDeclarationPayload(payload, node, inherited, type, rule, updateAllStyles)
+ {
+ if (!payload)
+ return null;
+
+ rule = rule || null;
+ inherited = inherited || false;
+
+ var id = payload.styleId;
+ var mapKey = id ? id.styleSheetId + ":" + id.ordinal : null;
+
+ if (type === WebInspector.CSSStyleDeclaration.Type.Attribute)
+ mapKey = node.id + ":attribute";
+
+ var styleDeclaration = rule ? rule.style : null;
+ var styleDeclarations = [];
+
+ // Look for existing styles in the previous map if there is one, otherwise use the current map.
+ var previousStyleDeclarationsMap = this._previousStyleDeclarationsMap || this._styleDeclarationsMap;
+ if (mapKey && mapKey in previousStyleDeclarationsMap) {
+ styleDeclarations = previousStyleDeclarationsMap[mapKey];
+
+ // If we need to update all styles, then stop here and call _parseStyleDeclarationPayload for each style.
+ // We need to parse multiple times so we reuse the right properties from each style.
+ if (updateAllStyles && styleDeclarations.length) {
+ for (var i = 0; i < styleDeclarations.length; ++i) {
+ var styleDeclaration = styleDeclarations[i];
+ this._parseStyleDeclarationPayload(payload, styleDeclaration.node, styleDeclaration.inherited, styleDeclaration.type, styleDeclaration.ownerRule);
+ }
+
+ return null;
+ }
+
+ if (!styleDeclaration) {
+ var filteredStyleDeclarations = styleDeclarations.filter(function(styleDeclaration) {
+ // This case only applies for styles that are not part of a rule.
+ if (styleDeclaration.ownerRule) {
+ console.assert(!rule);
+ return false;
+ }
+
+ if (styleDeclaration.node !== node)
+ return false;
+
+ if (styleDeclaration.inherited !== inherited)
+ return false;
+
+ return true;
+ });
+
+ console.assert(filteredStyleDeclarations.length <= 1);
+ styleDeclaration = filteredStyleDeclarations[0] || null;
+ }
+ }
+
+ if (previousStyleDeclarationsMap !== this._styleDeclarationsMap) {
+ // If the previous and current maps differ then make sure the found styleDeclaration is added to the current map.
+ styleDeclarations = mapKey && mapKey in this._styleDeclarationsMap ? this._styleDeclarationsMap[mapKey] : [];
+
+ if (styleDeclaration && !styleDeclarations.includes(styleDeclaration)) {
+ styleDeclarations.push(styleDeclaration);
+ this._styleDeclarationsMap[mapKey] = styleDeclarations;
+ }
+ }
+
+ var shorthands = {};
+ for (var i = 0; payload.shorthandEntries && i < payload.shorthandEntries.length; ++i) {
+ var shorthand = payload.shorthandEntries[i];
+ shorthands[shorthand.name] = shorthand.value;
+ }
+
+ var text = payload.cssText;
+
+ var inheritedPropertyCount = 0;
+
+ var properties = [];
+ for (var i = 0; payload.cssProperties && i < payload.cssProperties.length; ++i) {
+ var propertyPayload = payload.cssProperties[i];
+
+ if (inherited && WebInspector.CSSProperty.isInheritedPropertyName(propertyPayload.name))
+ ++inheritedPropertyCount;
+
+ var property = this._parseStylePropertyPayload(propertyPayload, i, styleDeclaration, text);
+ properties.push(property);
+ }
+
+ var styleSheetTextRange = this._parseSourceRangePayload(payload.range);
+
+ if (styleDeclaration) {
+ styleDeclaration.update(text, properties, styleSheetTextRange);
+ return styleDeclaration;
+ }
+
+ var styleSheet = id ? WebInspector.cssStyleManager.styleSheetForIdentifier(id.styleSheetId) : null;
+ if (styleSheet) {
+ if (type === WebInspector.CSSStyleDeclaration.Type.Inline)
+ styleSheet.markAsInlineStyleAttributeStyleSheet();
+ styleSheet.addEventListener(WebInspector.CSSStyleSheet.Event.ContentDidChange, this._styleSheetContentDidChange, this);
+ }
+
+ if (inherited && !inheritedPropertyCount)
+ return null;
+
+ styleDeclaration = new WebInspector.CSSStyleDeclaration(this, styleSheet, id, type, node, inherited, text, properties, styleSheetTextRange);
+
+ if (mapKey) {
+ styleDeclarations.push(styleDeclaration);
+ this._styleDeclarationsMap[mapKey] = styleDeclarations;
+ }
+
+ return styleDeclaration;
+ }
+
+ _parseSelectorListPayload(selectorList)
+ {
+ var selectors = selectorList.selectors;
+ if (!selectors.length)
+ return [];
+
+ // COMPATIBILITY (iOS 8): The selectorList payload was an array of selector text strings.
+ // Now they are CSSSelector objects with multiple properties.
+ if (typeof selectors[0] === "string") {
+ return selectors.map(function(selectorText) {
+ return new WebInspector.CSSSelector(selectorText);
+ });
+ }
+
+ return selectors.map(function(selectorPayload) {
+ return new WebInspector.CSSSelector(selectorPayload.text, selectorPayload.specificity, selectorPayload.dynamic);
+ });
+ }
+
+ _parseRulePayload(payload, matchedSelectorIndices, node, inherited, ruleOccurrences)
+ {
+ if (!payload)
+ return null;
+
+ // User and User Agent rules don't have 'ruleId' in the payload. However, their style's have 'styleId' and
+ // 'styleId' is the same identifier the backend uses for Author rule identifiers, so do the same here.
+ // They are excluded by the backend because they are not editable, however our front-end does not determine
+ // editability solely based on the existence of the id like the open source front-end does.
+ var id = payload.ruleId || payload.style.styleId;
+
+ var mapKey = id ? id.styleSheetId + ":" + id.ordinal + ":" + (inherited ? "I" : "N") + ":" + node.id : null;
+
+ // Rules can match multiple times if they have multiple selectors or because of inheritance. We keep a count
+ // of occurrences so we have unique rules per occurrence, that way properties will be correctly marked as overridden.
+ var occurrence = 0;
+ if (mapKey) {
+ if (mapKey in ruleOccurrences)
+ occurrence = ++ruleOccurrences[mapKey];
+ else
+ ruleOccurrences[mapKey] = occurrence;
+
+ // Append the occurrence number to the map key for lookup in the rules map.
+ mapKey += ":" + occurrence;
+ }
+
+ var rule = null;
+
+ // Look for existing rules in the previous map if there is one, otherwise use the current map.
+ var previousRulesMap = this._previousRulesMap || this._rulesMap;
+ if (mapKey && mapKey in previousRulesMap) {
+ rule = previousRulesMap[mapKey];
+
+ if (previousRulesMap !== this._rulesMap) {
+ // If the previous and current maps differ then make sure the found rule is added to the current map.
+ this._rulesMap[mapKey] = rule;
+ }
+ }
+
+ var style = this._parseStyleDeclarationPayload(payload.style, node, inherited, WebInspector.CSSStyleDeclaration.Type.Rule, rule);
+ if (!style)
+ return null;
+
+ var styleSheet = id ? WebInspector.cssStyleManager.styleSheetForIdentifier(id.styleSheetId) : null;
+
+ var selectorText = payload.selectorList.text;
+ var selectors = this._parseSelectorListPayload(payload.selectorList);
+ var type = WebInspector.CSSStyleManager.protocolStyleSheetOriginToEnum(payload.origin);
+
+ var sourceCodeLocation = null;
+ var sourceRange = payload.selectorList.range;
+ if (sourceRange)
+ sourceCodeLocation = this._createSourceCodeLocation(payload.sourceURL, sourceRange.startLine, sourceRange.startColumn);
+ else {
+ // FIXME: Is it possible for a CSSRule to have a sourceLine without its selectorList having a sourceRange? Fall back just in case.
+ sourceCodeLocation = this._createSourceCodeLocation(payload.sourceURL, payload.sourceLine);
+ }
+
+ if (styleSheet)
+ sourceCodeLocation = styleSheet.offsetSourceCodeLocation(sourceCodeLocation);
+
+ var mediaList = [];
+ for (var i = 0; payload.media && i < payload.media.length; ++i) {
+ var mediaItem = payload.media[i];
+ var mediaType = WebInspector.CSSStyleManager.protocolMediaSourceToEnum(mediaItem.source);
+ var mediaText = mediaItem.text;
+ var mediaSourceCodeLocation = this._createSourceCodeLocation(mediaItem.sourceURL, mediaItem.sourceLine);
+ if (styleSheet)
+ mediaSourceCodeLocation = styleSheet.offsetSourceCodeLocation(mediaSourceCodeLocation);
+
+ mediaList.push(new WebInspector.CSSMedia(mediaType, mediaText, mediaSourceCodeLocation));
+ }
+
+ if (rule) {
+ rule.update(sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, mediaList);
+ return rule;
+ }
+
+ if (styleSheet)
+ styleSheet.addEventListener(WebInspector.CSSStyleSheet.Event.ContentDidChange, this._styleSheetContentDidChange, this);
+
+ rule = new WebInspector.CSSRule(this, styleSheet, id, type, sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, mediaList);
+
+ if (mapKey)
+ this._rulesMap[mapKey] = rule;
+
+ return rule;
+ }
+
+ _markAsNeedsRefresh()
+ {
+ this._needsRefresh = true;
+ this.dispatchEventToListeners(WebInspector.DOMNodeStyles.Event.NeedsRefresh);
+ }
+
+ _styleSheetContentDidChange(event)
+ {
+ var styleSheet = event.target;
+ console.assert(styleSheet);
+ if (!styleSheet)
+ return;
+
+ // Ignore the stylesheet we know we just changed and handled above.
+ if (styleSheet === this._ignoreNextContentDidChangeForStyleSheet) {
+ delete this._ignoreNextContentDidChangeForStyleSheet;
+ return;
+ }
+
+ this._markAsNeedsRefresh();
+ }
+
+ _updateStyleCascade()
+ {
+ var cascadeOrderedStyleDeclarations = this._collectStylesInCascadeOrder(this._matchedRules, this._inlineStyle, this._attributesStyle);
+
+ for (var i = 0; i < this._inheritedRules.length; ++i) {
+ var inheritedStyleInfo = this._inheritedRules[i];
+ var inheritedCascadeOrder = this._collectStylesInCascadeOrder(inheritedStyleInfo.matchedRules, inheritedStyleInfo.inlineStyle, null);
+ cascadeOrderedStyleDeclarations = cascadeOrderedStyleDeclarations.concat(inheritedCascadeOrder);
+ }
+
+ this._orderedStyles = cascadeOrderedStyleDeclarations;
+
+ this._propertyNameToEffectivePropertyMap = {};
+
+ this._markOverriddenProperties(cascadeOrderedStyleDeclarations, this._propertyNameToEffectivePropertyMap);
+ this._associateRelatedProperties(cascadeOrderedStyleDeclarations, this._propertyNameToEffectivePropertyMap);
+
+ for (var pseudoIdentifier in this._pseudoElements) {
+ var pseudoElementInfo = this._pseudoElements[pseudoIdentifier];
+ pseudoElementInfo.orderedStyles = this._collectStylesInCascadeOrder(pseudoElementInfo.matchedRules, null, null);
+ this._markOverriddenProperties(pseudoElementInfo.orderedStyles);
+ this._associateRelatedProperties(pseudoElementInfo.orderedStyles);
+ }
+ }
+
+ _collectStylesInCascadeOrder(matchedRules, inlineStyle, attributesStyle)
+ {
+ var result = [];
+
+ // Inline style has the greatest specificity. So it goes first in the cascade order.
+ if (inlineStyle)
+ result.push(inlineStyle);
+
+ var userAndUserAgentStyles = [];
+
+ for (var i = 0; i < matchedRules.length; ++i) {
+ var rule = matchedRules[i];
+
+ // Only append to the result array here for author and inspector rules since attribute
+ // styles come between author rules and user/user agent rules.
+ switch (rule.type) {
+ case WebInspector.CSSStyleSheet.Type.Inspector:
+ case WebInspector.CSSStyleSheet.Type.Author:
+ result.push(rule.style);
+ break;
+
+ case WebInspector.CSSStyleSheet.Type.User:
+ case WebInspector.CSSStyleSheet.Type.UserAgent:
+ userAndUserAgentStyles.push(rule.style);
+ break;
+ }
+ }
+
+ // Style properties from HTML attributes are next.
+ if (attributesStyle)
+ result.push(attributesStyle);
+
+ // Finally add the user and user stylesheet's matched style rules we collected earlier.
+ result = result.concat(userAndUserAgentStyles);
+
+ return result;
+ }
+
+ _markOverriddenProperties(styles, propertyNameToEffectiveProperty)
+ {
+ propertyNameToEffectiveProperty = propertyNameToEffectiveProperty || {};
+
+ for (var i = 0; i < styles.length; ++i) {
+ var style = styles[i];
+ var properties = style.properties;
+
+ for (var j = 0; j < properties.length; ++j) {
+ var property = properties[j];
+ if (!property.enabled || !property.valid) {
+ property.overridden = false;
+ continue;
+ }
+
+ if (style.inherited && !property.inherited) {
+ property.overridden = false;
+ continue;
+ }
+
+ var canonicalName = property.canonicalName;
+ if (canonicalName in propertyNameToEffectiveProperty) {
+ var effectiveProperty = propertyNameToEffectiveProperty[canonicalName];
+
+ if (effectiveProperty.ownerStyle === property.ownerStyle) {
+ if (effectiveProperty.important && !property.important) {
+ property.overridden = true;
+ continue;
+ }
+ } else if (effectiveProperty.important || !property.important || effectiveProperty.ownerStyle.node !== property.ownerStyle.node) {
+ property.overridden = true;
+ continue;
+ }
+
+ if (!property.anonymous)
+ effectiveProperty.overridden = true;
+ }
+
+ property.overridden = false;
+
+ propertyNameToEffectiveProperty[canonicalName] = property;
+ }
+ }
+ }
+
+ _associateRelatedProperties(styles, propertyNameToEffectiveProperty)
+ {
+ for (var i = 0; i < styles.length; ++i) {
+ var properties = styles[i].properties;
+
+ var knownShorthands = {};
+
+ for (var j = 0; j < properties.length; ++j) {
+ var property = properties[j];
+
+ if (!property.valid)
+ continue;
+
+ if (!WebInspector.CSSCompletions.cssNameCompletions.isShorthandPropertyName(property.name))
+ continue;
+
+ if (knownShorthands[property.canonicalName] && !knownShorthands[property.canonicalName].overridden) {
+ console.assert(property.overridden);
+ continue;
+ }
+
+ knownShorthands[property.canonicalName] = property;
+ }
+
+ for (var j = 0; j < properties.length; ++j) {
+ var property = properties[j];
+
+ if (!property.valid)
+ continue;
+
+ var shorthandProperty = null;
+
+ if (!isEmptyObject(knownShorthands)) {
+ var possibleShorthands = WebInspector.CSSCompletions.cssNameCompletions.shorthandsForLonghand(property.canonicalName);
+ for (var k = 0; k < possibleShorthands.length; ++k) {
+ if (possibleShorthands[k] in knownShorthands) {
+ shorthandProperty = knownShorthands[possibleShorthands[k]];
+ break;
+ }
+ }
+ }
+
+ if (!shorthandProperty || shorthandProperty.overridden !== property.overridden) {
+ property.relatedShorthandProperty = null;
+ property.clearRelatedLonghandProperties();
+ continue;
+ }
+
+ shorthandProperty.addRelatedLonghandProperty(property);
+ property.relatedShorthandProperty = shorthandProperty;
+
+ if (propertyNameToEffectiveProperty && propertyNameToEffectiveProperty[shorthandProperty.canonicalName] === shorthandProperty)
+ propertyNameToEffectiveProperty[property.canonicalName] = property;
+ }
+ }
+ }
+
+ _isPropertyFoundInMatchingRules(propertyName)
+ {
+ return this._orderedStyles.some((style) => {
+ return style.properties.some((property) => property.name === propertyName);
+ });
+ }
+};
+
+WebInspector.DOMNodeStyles.Event = {
+ NeedsRefresh: "dom-node-styles-needs-refresh",
+ Refreshed: "dom-node-styles-refreshed"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/DOMSearchMatchObject.js b/Source/WebInspectorUI/UserInterface/Models/DOMSearchMatchObject.js
new file mode 100644
index 000000000..bf8b9308c
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DOMSearchMatchObject.js
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DOMSearchMatchObject = class DOMSearchMatchObject extends WebInspector.Object
+{
+ constructor(resource, domNode, title, searchTerm, textRange)
+ {
+ super();
+
+ console.assert(resource instanceof WebInspector.Resource);
+ console.assert(domNode instanceof WebInspector.DOMNode);
+
+ this._resource = resource;
+ this._domNode = domNode;
+ this._title = title;
+ this._searchTerm = searchTerm;
+ this._sourceCodeTextRange = resource.createSourceCodeTextRange(textRange);
+ }
+
+ // Static
+
+ static titleForDOMNode(domNode)
+ {
+ switch (domNode.nodeType()) {
+ case Node.ELEMENT_NODE:
+ var title = "<" + domNode.nodeNameInCorrectCase();
+ for (var attribute of domNode.attributes()) {
+ title += " " + attribute.name;
+ if (attribute.value.length)
+ title += "=\"" + attribute.value + "\"";
+ }
+ return title + ">";
+
+ case Node.TEXT_NODE:
+ return "\"" + domNode.nodeValue() + "\"";
+
+ case Node.COMMENT_NODE:
+ return "<!--" + domNode.nodeValue() + "-->";
+
+ case Node.DOCUMENT_TYPE_NODE:
+ var title = "<!DOCTYPE " + domNode.nodeName();
+ if (domNode.publicId) {
+ title += " PUBLIC \"" + domNode.publicId + "\"";
+ if (domNode.systemId)
+ title += " \"" + domNode.systemId + "\"";
+ } else if (domNode.systemId)
+ title += " SYSTEM \"" + domNode.systemId + "\"";
+
+ return title + ">";
+
+ case Node.CDATA_SECTION_NODE:
+ return "<![CDATA[" + domNode + "]]>";
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ var data = domNode.nodeValue();
+ var dataString = data.length ? " " + data : "";
+ var title = "<?" + domNode.nodeNameInCorrectCase() + dataString + "?>";
+ return title;
+
+ default:
+ console.error("Unknown DOM node type: ", domNode.nodeType());
+ return domNode.nodeNameInCorrectCase();
+ }
+ }
+
+ // Public
+
+ get resource()
+ {
+ return this._resource;
+ }
+
+ get domNode()
+ {
+ return this._domNode;
+ }
+
+ get title()
+ {
+ return this._title;
+ }
+
+ get className()
+ {
+ if (!this._className)
+ this._className = this._generateClassName();
+
+ return this._className;
+ }
+
+ get searchTerm()
+ {
+ return this._searchTerm;
+ }
+
+ get sourceCodeTextRange()
+ {
+ return this._sourceCodeTextRange;
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.DOMSearchMatchObject.URLCookieKey] = this._resource.url.hash;
+ cookie[WebInspector.DOMSearchMatchObject.TitleKey] = this._title;
+ var textRange = this._sourceCodeTextRange.textRange;
+ cookie[WebInspector.DOMSearchMatchObject.TextRangeKey] = [textRange.startLine, textRange.startColumn, textRange.endLine, textRange.endColumn].join();
+ }
+
+ // Private
+
+ _generateClassName()
+ {
+ switch (this._domNode.nodeType()) {
+ case Node.ELEMENT_NODE:
+ return WebInspector.DOMSearchMatchObject.DOMMatchElementIconStyleClassName;
+
+ case Node.TEXT_NODE:
+ return WebInspector.DOMSearchMatchObject.DOMMatchTextNodeIconStyleClassName;
+
+ case Node.COMMENT_NODE:
+ return WebInspector.DOMSearchMatchObject.DOMMatchCommentIconStyleClassName;
+
+ case Node.DOCUMENT_TYPE_NODE:
+ return WebInspector.DOMSearchMatchObject.DOMMatchDocumentTypeIconStyleClassName;
+
+ case Node.CDATA_SECTION_NODE:
+ return WebInspector.DOMSearchMatchObject.DOMMatchCharacterDataIconStyleClassName;
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ // <rdar://problem/12800950> Need icon for DOCUMENT_FRAGMENT_NODE and PROCESSING_INSTRUCTION_NODE
+ return WebInspector.DOMSearchMatchObject.DOMMatchDocumentTypeIconStyleClassName;
+
+ default:
+ console.error("Unknown DOM node type: ", this._domNode.nodeType());
+ return WebInspector.DOMSearchMatchObject.DOMMatchNodeIconStyleClassName;
+ }
+ }
+};
+
+WebInspector.DOMSearchMatchObject.DOMMatchElementIconStyleClassName = "dom-match-element-icon";
+WebInspector.DOMSearchMatchObject.DOMMatchTextNodeIconStyleClassName = "dom-match-text-node-icon";
+WebInspector.DOMSearchMatchObject.DOMMatchCommentIconStyleClassName = "dom-match-comment-icon";
+WebInspector.DOMSearchMatchObject.DOMMatchDocumentTypeIconStyleClassName = "dom-match-document-type-icon";
+WebInspector.DOMSearchMatchObject.DOMMatchCharacterDataIconStyleClassName = "dom-match-character-data-icon";
+WebInspector.DOMSearchMatchObject.DOMMatchNodeIconStyleClassName = "dom-match-node-icon";
+
+WebInspector.DOMSearchMatchObject.TypeIdentifier = "dom-search-match-object";
+WebInspector.DOMSearchMatchObject.URLCookieKey = "resource-url";
+WebInspector.DOMSearchMatchObject.TitleKey = "title";
+WebInspector.DOMSearchMatchObject.TextRangeKey = "text-range";
diff --git a/Source/WebInspectorUI/UserInterface/Models/DOMStorageObject.js b/Source/WebInspectorUI/UserInterface/Models/DOMStorageObject.js
new file mode 100644
index 000000000..ac03b8d1b
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DOMStorageObject.js
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DOMStorageObject = class DOMStorageObject extends WebInspector.Object
+{
+ constructor(id, host, isLocalStorage)
+ {
+ super();
+
+ this._id = id;
+ this._host = host;
+ this._isLocalStorage = isLocalStorage;
+ this._entries = new Map;
+ }
+
+ // Public
+
+ get id() { return this._id; }
+ get host() { return this._host; }
+ get entries() { return this._entries; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.DOMStorageObject.HostCookieKey] = this.host;
+ cookie[WebInspector.DOMStorageObject.LocalStorageCookieKey] = this.isLocalStorage();
+ }
+
+ isLocalStorage()
+ {
+ return this._isLocalStorage;
+ }
+
+ getEntries(callback)
+ {
+ function innerCallback(error, entries)
+ {
+ if (error)
+ return;
+
+ for (let [key, value] of entries) {
+ if (!key || !value)
+ continue;
+
+ this._entries.set(key, value);
+ }
+
+ callback(error, entries);
+ }
+
+ DOMStorageAgent.getDOMStorageItems(this._id, innerCallback.bind(this));
+ }
+
+ removeItem(key)
+ {
+ DOMStorageAgent.removeDOMStorageItem(this._id, key);
+ }
+
+ setItem(key, value)
+ {
+ DOMStorageAgent.setDOMStorageItem(this._id, key, value);
+ }
+
+ itemsCleared()
+ {
+ this._entries.clear();
+ this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemsCleared);
+ }
+
+ itemRemoved(key)
+ {
+ this._entries.delete(key);
+ this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemRemoved, {key});
+ }
+
+ itemAdded(key, value)
+ {
+ this._entries.set(key, value);
+ this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemAdded, {key, value});
+ }
+
+ itemUpdated(key, oldValue, value)
+ {
+ this._entries.set(key, value);
+ this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemUpdated, {key, oldValue, value});
+ }
+};
+
+WebInspector.DOMStorageObject.TypeIdentifier = "dom-storage";
+WebInspector.DOMStorageObject.HostCookieKey = "dom-storage-object-host";
+WebInspector.DOMStorageObject.LocalStorageCookieKey = "dom-storage-object-local-storage";
+
+WebInspector.DOMStorageObject.Event = {
+ ItemsCleared: "dom-storage-object-items-cleared",
+ ItemAdded: "dom-storage-object-item-added",
+ ItemRemoved: "dom-storage-object-item-removed",
+ ItemUpdated: "dom-storage-object-updated",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/DOMTree.js b/Source/WebInspectorUI/UserInterface/Models/DOMTree.js
new file mode 100644
index 000000000..dfabd481f
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DOMTree.js
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DOMTree = class DOMTree extends WebInspector.Object
+{
+ constructor(frame)
+ {
+ super();
+
+ this._frame = frame;
+
+ this._rootDOMNode = null;
+ this._requestIdentifier = 0;
+ this._contentFlowCollection = new WebInspector.Collection(WebInspector.Collection.TypeVerifier.ContentFlow);
+
+ this._frame.addEventListener(WebInspector.Frame.Event.PageExecutionContextChanged, this._framePageExecutionContextChanged, this);
+
+ WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.DocumentUpdated, this._documentUpdated, this);
+
+ // Only add extra event listeners when not the main frame. Since DocumentUpdated is enough for the main frame.
+ if (!this._frame.isMainFrame()) {
+ WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.NodeRemoved, this._nodeRemoved, this);
+ this._frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._frameMainResourceDidChange, this);
+ }
+
+ WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.ContentFlowListWasUpdated, this._contentFlowListWasUpdated, this);
+ WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.ContentFlowWasAdded, this._contentFlowWasAdded, this);
+ WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.ContentFlowWasRemoved, this._contentFlowWasRemoved, this);
+ }
+
+ // Public
+
+ get frame() { return this._frame; }
+ get contentFlowCollection() { return this._contentFlowCollection; }
+
+ disconnect()
+ {
+ WebInspector.domTreeManager.removeEventListener(null, null, this);
+ this._frame.removeEventListener(null, null, this);
+ }
+
+ invalidate()
+ {
+ // Set to null so it is fetched again next time requestRootDOMNode is called.
+ this._rootDOMNode = null;
+
+ // Clear the pending callbacks. It is the responsibility of the client to listen for
+ // the RootDOMNodeInvalidated event and request the root DOM node again.
+ this._pendingRootDOMNodeRequests = null;
+
+ if (this._invalidateTimeoutIdentifier)
+ return;
+
+ function performInvalidate()
+ {
+ this._invalidateTimeoutIdentifier = undefined;
+
+ this.dispatchEventToListeners(WebInspector.DOMTree.Event.RootDOMNodeInvalidated);
+ }
+
+ // Delay the invalidation on a timeout to coalesce multiple calls to invalidate.
+ this._invalidateTimeoutIdentifier = setTimeout(performInvalidate.bind(this), 0);
+ }
+
+ requestRootDOMNode(callback)
+ {
+ console.assert(typeof callback === "function");
+ if (typeof callback !== "function")
+ return;
+
+ if (this._rootDOMNode) {
+ callback(this._rootDOMNode);
+ return;
+ }
+
+ if (!this._frame.isMainFrame() && !this._frame.pageExecutionContext) {
+ this._rootDOMNodeRequestWaitingForExecutionContext = true;
+ if (!this._pendingRootDOMNodeRequests)
+ this._pendingRootDOMNodeRequests = [];
+ this._pendingRootDOMNodeRequests.push(callback);
+ return;
+ }
+
+ if (this._pendingRootDOMNodeRequests) {
+ this._pendingRootDOMNodeRequests.push(callback);
+ return;
+ }
+
+ this._pendingRootDOMNodeRequests = [callback];
+ this._requestRootDOMNode();
+ }
+
+ requestContentFlowList()
+ {
+ this.requestRootDOMNode(function(rootNode) {
+ // Let the backend know we are interested about the named flow events for this document.
+ WebInspector.domTreeManager.getNamedFlowCollection(rootNode.id);
+ });
+ }
+
+ // Private
+
+ _requestRootDOMNode()
+ {
+ console.assert(this._frame.isMainFrame() || this._frame.pageExecutionContext);
+ console.assert(this._pendingRootDOMNodeRequests.length);
+
+ // Bump the request identifier. This prevents pending callbacks for previous requests from completing.
+ var requestIdentifier = ++this._requestIdentifier;
+
+ function rootObjectAvailable(error, result)
+ {
+ // Check to see if we have been invalidated (if the callbacks were cleared).
+ if (!this._pendingRootDOMNodeRequests || requestIdentifier !== this._requestIdentifier)
+ return;
+
+ if (error) {
+ console.error(JSON.stringify(error));
+
+ this._rootDOMNode = null;
+ dispatchCallbacks.call(this);
+ return;
+ }
+
+ // Convert the RemoteObject to a DOMNode by asking the backend to push it to us.
+ var remoteObject = WebInspector.RemoteObject.fromPayload(result);
+ remoteObject.pushNodeToFrontend(rootDOMNodeAvailable.bind(this, remoteObject));
+ }
+
+ function rootDOMNodeAvailable(remoteObject, nodeId)
+ {
+ remoteObject.release();
+
+ // Check to see if we have been invalidated (if the callbacks were cleared).
+ if (!this._pendingRootDOMNodeRequests || requestIdentifier !== this._requestIdentifier)
+ return;
+
+ if (!nodeId) {
+ this._rootDOMNode = null;
+ dispatchCallbacks.call(this);
+ return;
+ }
+
+ this._rootDOMNode = WebInspector.domTreeManager.nodeForId(nodeId);
+
+ console.assert(this._rootDOMNode);
+ if (!this._rootDOMNode) {
+ dispatchCallbacks.call(this);
+ return;
+ }
+
+ // Request the child nodes since the root node is often not shown in the UI,
+ // and the child nodes will be needed immediately.
+ this._rootDOMNode.getChildNodes(dispatchCallbacks.bind(this));
+ }
+
+ function mainDocumentAvailable(document)
+ {
+ this._rootDOMNode = document;
+
+ dispatchCallbacks.call(this);
+ }
+
+ function dispatchCallbacks()
+ {
+ // Check to see if we have been invalidated (if the callbacks were cleared).
+ if (!this._pendingRootDOMNodeRequests || requestIdentifier !== this._requestIdentifier)
+ return;
+
+ for (var i = 0; i < this._pendingRootDOMNodeRequests.length; ++i)
+ this._pendingRootDOMNodeRequests[i](this._rootDOMNode);
+ this._pendingRootDOMNodeRequests = null;
+ }
+
+ // For the main frame we can use the more straight forward requestDocument function. For
+ // child frames we need to do a more roundabout approach since the protocol does not include
+ // a specific way to request a document given a frame identifier. The child frame approach
+ // involves evaluating the JavaScript "document" and resolving that into a DOMNode.
+ if (this._frame.isMainFrame())
+ WebInspector.domTreeManager.requestDocument(mainDocumentAvailable.bind(this));
+ else {
+ var contextId = this._frame.pageExecutionContext.id;
+ RuntimeAgent.evaluate.invoke({expression: appendWebInspectorSourceURL("document"), objectGroup: "", includeCommandLineAPI: false, doNotPauseOnExceptionsAndMuteConsole: true, contextId, returnByValue: false, generatePreview: false}, rootObjectAvailable.bind(this));
+ }
+ }
+
+ _nodeRemoved(event)
+ {
+ console.assert(!this._frame.isMainFrame());
+
+ if (event.data.node !== this._rootDOMNode)
+ return;
+
+ this.invalidate();
+ }
+
+ _documentUpdated(event)
+ {
+ this.invalidate();
+ }
+
+ _frameMainResourceDidChange(event)
+ {
+ console.assert(!this._frame.isMainFrame());
+
+ this.invalidate();
+ }
+
+ _framePageExecutionContextChanged(event)
+ {
+ if (this._rootDOMNodeRequestWaitingForExecutionContext) {
+ console.assert(this._frame.pageExecutionContext);
+ console.assert(this._pendingRootDOMNodeRequests && this._pendingRootDOMNodeRequests.length);
+
+ this._rootDOMNodeRequestWaitingForExecutionContext = false;
+
+ this._requestRootDOMNode();
+ }
+ }
+
+ _isContentFlowInCurrentDocument(flow)
+ {
+ return this._rootDOMNode && this._rootDOMNode.id === flow.documentNodeIdentifier;
+ }
+
+ _contentFlowListWasUpdated(event)
+ {
+ if (!this._rootDOMNode || this._rootDOMNode.id !== event.data.documentNodeIdentifier)
+ return;
+
+ // Assume that all the flows have been removed.
+ let deletedFlows = new Set(this._contentFlowCollection.items);
+ let newFlows = new Set;
+ for (let flow of event.data.flows) {
+ // All the flows received from WebKit are part of the same document.
+ console.assert(this._isContentFlowInCurrentDocument(flow));
+
+ if (this._contentFlowCollection.items.has(flow)) {
+ // Remove the flow name from the deleted list.
+ console.assert(deletedFlows.has(flow));
+ deletedFlows.delete(flow);
+ } else {
+ this._contentFlowCollection.add(flow);
+ newFlows.add(flow);
+ }
+ }
+
+ for (let flow of deletedFlows)
+ this._contentFlowCollection.remove(flow);
+
+ // Send update events to listeners.
+
+ for (let flow of deletedFlows)
+ this.dispatchEventToListeners(WebInspector.DOMTree.Event.ContentFlowWasRemoved, {flow});
+
+ for (let flow of newFlows)
+ this.dispatchEventToListeners(WebInspector.DOMTree.Event.ContentFlowWasAdded, {flow});
+ }
+
+ _contentFlowWasAdded(event)
+ {
+ let flow = event.data.flow;
+ if (!this._isContentFlowInCurrentDocument(flow))
+ return;
+
+ this._contentFlowCollection.add(flow);
+
+ this.dispatchEventToListeners(WebInspector.DOMTree.Event.ContentFlowWasAdded, {flow});
+ }
+
+ _contentFlowWasRemoved(event)
+ {
+ let flow = event.data.flow;
+ if (!this._isContentFlowInCurrentDocument(flow))
+ return;
+
+ this._contentFlowCollection.remove(flow);
+
+ this.dispatchEventToListeners(WebInspector.DOMTree.Event.ContentFlowWasRemoved, {flow});
+ }
+};
+
+WebInspector.DOMTree.Event = {
+ RootDOMNodeInvalidated: "dom-tree-root-dom-node-invalidated",
+ ContentFlowWasAdded: "dom-tree-content-flow-was-added",
+ ContentFlowWasRemoved: "dom-tree-content-flow-was-removed"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/DatabaseObject.js b/Source/WebInspectorUI/UserInterface/Models/DatabaseObject.js
new file mode 100644
index 000000000..961e41aa9
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DatabaseObject.js
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DatabaseObject = class DatabaseObject extends WebInspector.Object
+{
+ constructor(id, host, name, version)
+ {
+ super();
+
+ this._id = id;
+ this._host = host ? host : WebInspector.UIString("Local File");
+ this._name = name;
+ this._version = version;
+ }
+
+ // Public
+
+ get id() { return this._id; }
+ get host() { return this._host; }
+ get name() { return this._name; }
+ get version() { return this._version; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.DatabaseObject.HostCookieKey] = this.host;
+ cookie[WebInspector.DatabaseObject.NameCookieKey] = this.name;
+ }
+
+ getTableNames(callback)
+ {
+ function sortingCallback(error, names)
+ {
+ if (!error)
+ callback(names.sort());
+ }
+
+ DatabaseAgent.getDatabaseTableNames(this._id, sortingCallback);
+ }
+
+ executeSQL(query, successCallback, errorCallback)
+ {
+ function queryCallback(error, columnNames, values, sqlError)
+ {
+ if (error) {
+ errorCallback(WebInspector.UIString("An unexpected error occurred."));
+ return;
+ }
+
+ if (sqlError) {
+ switch (sqlError.code) {
+ case SQLException.VERSION_ERR:
+ errorCallback(WebInspector.UIString("Database no longer has expected version."));
+ break;
+ case SQLException.TOO_LARGE_ERR:
+ errorCallback(WebInspector.UIString("Data returned from the database is too large."));
+ break;
+ default:
+ errorCallback(WebInspector.UIString("An unexpected error occurred."));
+ break;
+ }
+ return;
+ }
+
+ successCallback(columnNames, values);
+ }
+
+ DatabaseAgent.executeSQL(this._id, query, queryCallback);
+ }
+};
+
+WebInspector.DatabaseObject.TypeIdentifier = "database";
+WebInspector.DatabaseObject.HostCookieKey = "database-object-host";
+WebInspector.DatabaseObject.NameCookieKey = "database-object-name";
diff --git a/Source/WebInspectorUI/UserInterface/Models/DatabaseTableObject.js b/Source/WebInspectorUI/UserInterface/Models/DatabaseTableObject.js
new file mode 100644
index 000000000..98513eb74
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DatabaseTableObject.js
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DatabaseTableObject = class DatabaseTableObject extends WebInspector.Object
+{
+ constructor(name, database)
+ {
+ super();
+
+ console.assert(database instanceof WebInspector.DatabaseObject);
+
+ this._name = name;
+ this._database = database;
+ }
+
+ // Public
+
+ get name() { return this._name; }
+ get database() { return this._database; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.DatabaseTableObject.NameCookieKey] = this.name;
+ }
+};
+
+WebInspector.DatabaseTableObject.TypeIdentifier = "database-table";
+WebInspector.DatabaseTableObject.NameCookieKey = "database-table-object-name";
diff --git a/Source/WebInspectorUI/UserInterface/Models/DebuggerDashboard.js b/Source/WebInspectorUI/UserInterface/Models/DebuggerDashboard.js
new file mode 100644
index 000000000..0c4258c91
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DebuggerDashboard.js
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DebuggerDashboard = class DebuggerDashboard extends WebInspector.Object
+{
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/DebuggerData.js b/Source/WebInspectorUI/UserInterface/Models/DebuggerData.js
new file mode 100644
index 000000000..394b8ed45
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DebuggerData.js
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DebuggerData = class DebuggerData extends WebInspector.Object
+{
+ constructor(target)
+ {
+ super();
+
+ console.assert(target instanceof WebInspector.Target);
+
+ this._target = target;
+
+ this._paused = false;
+ this._pausing = false;
+ this._pauseReason = null;
+ this._pauseData = null;
+ this._callFrames = [];
+ this._asyncStackTrace = null;
+
+ this._scriptIdMap = new Map;
+ this._scriptContentIdentifierMap = new Map;
+
+ this._makePausingAfterNextResume = false;
+ }
+
+ // Public
+
+ get target() { return this._target; }
+ get paused() { return this._paused; }
+ get pausing() { return this._pausing; }
+ get pauseReason() { return this._pauseReason; }
+ get pauseData() { return this._pauseData; }
+ get callFrames() { return this._callFrames; }
+ get asyncStackTrace() { return this._asyncStackTrace; }
+
+ get scripts()
+ {
+ return Array.from(this._scriptIdMap.values());
+ }
+
+ scriptForIdentifier(id)
+ {
+ return this._scriptIdMap.get(id);
+ }
+
+ scriptsForURL(url)
+ {
+ return this._scriptContentIdentifierMap.get(url) || [];
+ }
+
+ // Protected (Called by DebuggerManager)
+
+ reset()
+ {
+ this._scriptIdMap.clear();
+ }
+
+ addScript(script)
+ {
+ this._scriptIdMap.set(script.id, script);
+
+ if (script.contentIdentifier) {
+ let scripts = this._scriptContentIdentifierMap.get(script.contentIdentifier);
+ if (!scripts) {
+ scripts = [];
+ this._scriptContentIdentifierMap.set(script.contentIdentifier, scripts);
+ }
+ scripts.push(script);
+ }
+ }
+
+ pauseIfNeeded()
+ {
+ if (this._paused || this._pausing)
+ return Promise.resolve();
+
+ this._pausing = true;
+
+ return this._target.DebuggerAgent.pause();
+ }
+
+ resumeIfNeeded()
+ {
+ if (!this._paused && !this._pausing)
+ return Promise.resolve();
+
+ this._pausing = false;
+
+ return this._target.DebuggerAgent.resume();
+ }
+
+ continueUntilNextRunLoop()
+ {
+ if (!this._paused || this._pausing)
+ return Promise.resolve();
+
+ // The backend will automatically start pausing
+ // after resuming, so we need to match that here.
+ this._makePausingAfterNextResume = true;
+
+ return this._target.DebuggerAgent.continueUntilNextRunLoop();
+ }
+
+ updateForPause(callFrames, pauseReason, pauseData, asyncStackTrace)
+ {
+ this._paused = true;
+ this._pausing = false;
+ this._pauseReason = pauseReason;
+ this._pauseData = pauseData;
+ this._callFrames = callFrames;
+ this._asyncStackTrace = asyncStackTrace;
+
+ // We paused, no need for auto-pausing.
+ this._makePausingAfterNextResume = false;
+ }
+
+ updateForResume()
+ {
+ this._paused = false;
+ this._pausing = false;
+ this._pauseReason = null;
+ this._pauseData = null;
+ this._callFrames = [];
+ this._asyncStackTrace = null;
+
+ // We resumed, but may be auto-pausing.
+ if (this._makePausingAfterNextResume) {
+ this._makePausingAfterNextResume = false;
+ this._pausing = true;
+ }
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/DefaultDashboard.js b/Source/WebInspectorUI/UserInterface/Models/DefaultDashboard.js
new file mode 100644
index 000000000..33b8d6d8a
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/DefaultDashboard.js
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.DefaultDashboard = class DefaultDashboard extends WebInspector.Object
+{
+ constructor()
+ {
+ super();
+
+ this._waitingForFirstMainResourceToStartTrackingSize = true;
+
+ // Necessary event required to track page load time and resource sizes.
+ WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
+ WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
+
+ // Necessary events required to track load of resources.
+ WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
+ WebInspector.Target.addEventListener(WebInspector.Target.Event.ResourceAdded, this._resourceWasAdded, this);
+ WebInspector.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.FrameWasAdded, this._frameWasAdded, this);
+
+ // Necessary events required to track console messages.
+ var logManager = WebInspector.logManager;
+ logManager.addEventListener(WebInspector.LogManager.Event.Cleared, this._consoleWasCleared, this);
+ logManager.addEventListener(WebInspector.LogManager.Event.MessageAdded, this._consoleMessageAdded, this);
+ logManager.addEventListener(WebInspector.LogManager.Event.PreviousMessageRepeatCountUpdated, this._consoleMessageWasRepeated, this);
+
+ this._resourcesCount = 0;
+ this._resourcesSize = 0;
+ this._time = 0;
+ this._logs = 0;
+ this._errors = 0;
+ this._issues = 0;
+ }
+
+ // Public
+
+ get resourcesCount()
+ {
+ return this._resourcesCount;
+ }
+
+ set resourcesCount(value)
+ {
+ this._resourcesCount = value;
+ this._dataDidChange();
+ }
+
+ get resourcesSize()
+ {
+ return this._resourcesSize;
+ }
+
+ set resourcesSize(value)
+ {
+ this._resourcesSize = value;
+ this._dataDidChange();
+ }
+
+ get time()
+ {
+ return this._time;
+ }
+
+ set time(value)
+ {
+ this._time = value;
+ this._dataDidChange();
+ }
+
+ get logs()
+ {
+ return this._logs;
+ }
+
+ set logs(value)
+ {
+ this._logs = value;
+ this._dataDidChange();
+ }
+
+ get errors()
+ {
+ return this._errors;
+ }
+
+ set errors(value)
+ {
+ this._errors = value;
+ this._dataDidChange();
+ }
+
+ get issues()
+ {
+ return this._issues;
+ }
+
+ set issues(value)
+ {
+ this._issues = value;
+ this._dataDidChange();
+ }
+
+ // Private
+
+ _dataDidChange()
+ {
+ this.dispatchEventToListeners(WebInspector.DefaultDashboard.Event.DataDidChange);
+ }
+
+ _mainResourceDidChange(event)
+ {
+ console.assert(event.target instanceof WebInspector.Frame);
+
+ if (!event.target.isMainFrame())
+ return;
+
+ this._time = 0;
+ this._resourcesCount = 1;
+ this._resourcesSize = WebInspector.frameResourceManager.mainFrame.mainResource.size || 0;
+
+ // We should only track resource sizes on fresh loads.
+ if (this._waitingForFirstMainResourceToStartTrackingSize) {
+ this._waitingForFirstMainResourceToStartTrackingSize = false;
+ WebInspector.Resource.addEventListener(WebInspector.Resource.Event.SizeDidChange, this._resourceSizeDidChange, this);
+ }
+
+ this._dataDidChange();
+ this._startUpdatingTime();
+ }
+
+ _capturingStopped(event)
+ {
+ // If recording stops, we should stop the timer if it hasn't stopped already.
+ this._stopUpdatingTime();
+ }
+
+ _resourceWasAdded(event)
+ {
+ ++this.resourcesCount;
+ }
+
+ _frameWasAdded(event)
+ {
+ ++this.resourcesCount;
+ }
+
+ _resourceSizeDidChange(event)
+ {
+ if (event.target.urlComponents.scheme === "data")
+ return;
+ this.resourcesSize += event.target.size - event.data.previousSize;
+ }
+
+ _startUpdatingTime()
+ {
+ this._stopUpdatingTime();
+
+ this.time = 0;
+
+ this._timelineBaseTime = Date.now();
+ this._timeIntervalDelay = 50;
+ this._timeIntervalIdentifier = setInterval(this._updateTime.bind(this), this._timeIntervalDelay);
+ }
+
+ _stopUpdatingTime()
+ {
+ if (!this._timeIntervalIdentifier)
+ return;
+
+ clearInterval(this._timeIntervalIdentifier);
+ this._timeIntervalIdentifier = undefined;
+ }
+
+ _updateTime()
+ {
+ var duration = Date.now() - this._timelineBaseTime;
+
+ var timeIntervalDelay = this._timeIntervalDelay;
+ if (duration >= 1000) // 1 second
+ timeIntervalDelay = 100;
+ else if (duration >= 60000) // 60 seconds
+ timeIntervalDelay = 1000;
+ else if (duration >= 3600000) // 1 minute
+ timeIntervalDelay = 10000;
+
+ if (timeIntervalDelay !== this._timeIntervalDelay) {
+ this._timeIntervalDelay = timeIntervalDelay;
+
+ clearInterval(this._timeIntervalIdentifier);
+ this._timeIntervalIdentifier = setInterval(this._updateTime.bind(this), this._timeIntervalDelay);
+ }
+
+ var mainFrame = WebInspector.frameResourceManager.mainFrame;
+ var mainFrameStartTime = mainFrame.mainResource.firstTimestamp;
+ var mainFrameLoadEventTime = mainFrame.loadEventTimestamp;
+
+ if (isNaN(mainFrameStartTime) || isNaN(mainFrameLoadEventTime)) {
+ this.time = duration / 1000;
+ return;
+ }
+
+ this.time = mainFrameLoadEventTime - mainFrameStartTime;
+
+ this._stopUpdatingTime();
+ }
+
+ _consoleMessageAdded(event)
+ {
+ var message = event.data.message;
+ this._lastConsoleMessageType = message.level;
+ this._incrementConsoleMessageType(message.level, message.repeatCount);
+ }
+
+ _consoleMessageWasRepeated(event)
+ {
+ this._incrementConsoleMessageType(this._lastConsoleMessageType, 1);
+ }
+
+ _incrementConsoleMessageType(type, increment)
+ {
+ switch (type) {
+ case WebInspector.ConsoleMessage.MessageLevel.Log:
+ case WebInspector.ConsoleMessage.MessageLevel.Info:
+ case WebInspector.ConsoleMessage.MessageLevel.Debug:
+ this.logs += increment;
+ break;
+ case WebInspector.ConsoleMessage.MessageLevel.Warning:
+ this.issues += increment;
+ break;
+ case WebInspector.ConsoleMessage.MessageLevel.Error:
+ this.errors += increment;
+ break;
+ }
+ }
+
+ _consoleWasCleared(event)
+ {
+ this._logs = 0;
+ this._issues = 0;
+ this._errors = 0;
+ this._dataDidChange();
+ }
+};
+
+WebInspector.DefaultDashboard.Event = {
+ DataDidChange: "default-dashboard-data-did-change"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ExecutionContext.js b/Source/WebInspectorUI/UserInterface/Models/ExecutionContext.js
new file mode 100644
index 000000000..383fc8173
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ExecutionContext.js
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ExecutionContext = class ExecutionContext extends WebInspector.Object
+{
+ constructor(target, id, name, isPageContext, frame)
+ {
+ super();
+
+ console.assert(target instanceof WebInspector.Target);
+ console.assert(typeof id === "number" || id === WebInspector.RuntimeManager.TopLevelExecutionContextIdentifier);
+ console.assert(typeof name === "string");
+
+ this._target = target;
+ this._id = id;
+ this._name = name;
+ this._isPageContext = isPageContext || false;
+ this._frame = frame || null;
+ }
+
+ // Public
+
+ get target() { return this._target; }
+ get id() { return this._id; }
+ get name() { return this._name; }
+ get isPageContext() { return this._isPageContext; }
+ get frame() { return this._frame; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ExecutionContextList.js b/Source/WebInspectorUI/UserInterface/Models/ExecutionContextList.js
new file mode 100644
index 000000000..e094fc742
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ExecutionContextList.js
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ExecutionContextList = class ExecutionContextList extends WebInspector.Object
+{
+ constructor()
+ {
+ super();
+
+ this._contexts = [];
+ this._pageExecutionContext = null;
+ }
+
+ // Public
+
+ get pageExecutionContext()
+ {
+ return this._pageExecutionContext;
+ }
+
+ get contexts()
+ {
+ return this._contexts;
+ }
+
+ add(context)
+ {
+ // FIXME: The backend sends duplicate page context execution contexts with the same id. Why?
+ if (context.isPageContext && this._pageExecutionContext) {
+ console.assert(context.id === this._pageExecutionContext.id);
+ return false;
+ }
+
+ this._contexts.push(context);
+
+ if (context.isPageContext) {
+ console.assert(!this._pageExecutionContext);
+ this._pageExecutionContext = context;
+ return true;
+ }
+
+ return false;
+ }
+
+ clear()
+ {
+ this._contexts = [];
+ this._pageExecutionContext = null;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/FPSInstrument.js b/Source/WebInspectorUI/UserInterface/Models/FPSInstrument.js
new file mode 100644
index 000000000..a5e4452a7
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/FPSInstrument.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.FPSInstrument = class FPSInstrument extends WebInspector.Instrument
+{
+ constructor()
+ {
+ super();
+
+ console.assert(WebInspector.FPSInstrument.supported());
+ }
+
+ // Static
+
+ static supported()
+ {
+ // COMPATIBILITY (iOS 8): TimelineAgent.EventType.RenderingFrame did not exist.
+ return window.TimelineAgent && TimelineAgent.EventType.RenderingFrame;
+ }
+
+ // Protected
+
+ get timelineRecordType()
+ {
+ return WebInspector.TimelineRecord.Type.RenderingFrame;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Frame.js b/Source/WebInspectorUI/UserInterface/Models/Frame.js
new file mode 100644
index 000000000..d32333a54
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Frame.js
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Frame = class Frame extends WebInspector.Object
+{
+ constructor(id, name, securityOrigin, loaderIdentifier, mainResource)
+ {
+ super();
+
+ console.assert(id);
+
+ this._id = id;
+
+ this._name = null;
+ this._securityOrigin = null;
+
+ this._resourceCollection = new WebInspector.ResourceCollection;
+ this._provisionalResourceCollection = new WebInspector.ResourceCollection;
+ this._extraScriptCollection = new WebInspector.Collection(WebInspector.Collection.TypeVerifier.Script);
+
+ this._childFrameCollection = new WebInspector.Collection(WebInspector.Collection.TypeVerifier.Frame);
+ this._childFrameIdentifierMap = new Map;
+
+ this._parentFrame = null;
+ this._isMainFrame = false;
+
+ this._domContentReadyEventTimestamp = NaN;
+ this._loadEventTimestamp = NaN;
+
+ this._executionContextList = new WebInspector.ExecutionContextList;
+
+ this.initialize(name, securityOrigin, loaderIdentifier, mainResource);
+ }
+
+ // Public
+
+ get resourceCollection() { return this._resourceCollection; }
+ get extraScriptCollection() { return this._extraScriptCollection; }
+ get childFrameCollection() { return this._childFrameCollection; }
+
+ initialize(name, securityOrigin, loaderIdentifier, mainResource)
+ {
+ console.assert(loaderIdentifier);
+ console.assert(mainResource);
+
+ var oldName = this._name;
+ var oldSecurityOrigin = this._securityOrigin;
+ var oldMainResource = this._mainResource;
+
+ this._name = name || null;
+ this._securityOrigin = securityOrigin || null;
+ this._loaderIdentifier = loaderIdentifier || null;
+
+ this._mainResource = mainResource;
+ this._mainResource._parentFrame = this;
+
+ if (oldMainResource && this._mainResource !== oldMainResource)
+ this._disassociateWithResource(oldMainResource);
+
+ this.removeAllResources();
+ this.removeAllChildFrames();
+ this.clearExecutionContexts();
+ this.clearProvisionalLoad();
+
+ if (this._mainResource !== oldMainResource)
+ this._dispatchMainResourceDidChangeEvent(oldMainResource);
+
+ if (this._securityOrigin !== oldSecurityOrigin)
+ this.dispatchEventToListeners(WebInspector.Frame.Event.SecurityOriginDidChange, {oldSecurityOrigin});
+
+ if (this._name !== oldName)
+ this.dispatchEventToListeners(WebInspector.Frame.Event.NameDidChange, {oldName});
+ }
+
+ startProvisionalLoad(provisionalMainResource)
+ {
+ console.assert(provisionalMainResource);
+
+ this._provisionalMainResource = provisionalMainResource;
+ this._provisionalMainResource._parentFrame = this;
+
+ this._provisionalLoaderIdentifier = provisionalMainResource.loaderIdentifier;
+
+ this._provisionalResourceCollection.clear();
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ProvisionalLoadStarted);
+ }
+
+ commitProvisionalLoad(securityOrigin)
+ {
+ console.assert(this._provisionalMainResource);
+ console.assert(this._provisionalLoaderIdentifier);
+ if (!this._provisionalLoaderIdentifier)
+ return;
+
+ var oldSecurityOrigin = this._securityOrigin;
+ var oldMainResource = this._mainResource;
+
+ this._securityOrigin = securityOrigin || null;
+ this._loaderIdentifier = this._provisionalLoaderIdentifier;
+ this._mainResource = this._provisionalMainResource;
+
+ this._domContentReadyEventTimestamp = NaN;
+ this._loadEventTimestamp = NaN;
+
+ if (oldMainResource && this._mainResource !== oldMainResource)
+ this._disassociateWithResource(oldMainResource);
+
+ this.removeAllResources();
+
+ this._resourceCollection = this._provisionalResourceCollection;
+ this._provisionalResourceCollection = new WebInspector.ResourceCollection;
+ this._extraScriptCollection.clear();
+
+ this.clearExecutionContexts(true);
+ this.clearProvisionalLoad(true);
+ this.removeAllChildFrames();
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ProvisionalLoadCommitted);
+
+ if (this._mainResource !== oldMainResource)
+ this._dispatchMainResourceDidChangeEvent(oldMainResource);
+
+ if (this._securityOrigin !== oldSecurityOrigin)
+ this.dispatchEventToListeners(WebInspector.Frame.Event.SecurityOriginDidChange, {oldSecurityOrigin});
+ }
+
+ clearProvisionalLoad(skipProvisionalLoadClearedEvent)
+ {
+ if (!this._provisionalLoaderIdentifier)
+ return;
+
+ this._provisionalLoaderIdentifier = null;
+ this._provisionalMainResource = null;
+ this._provisionalResourceCollection.clear();
+
+ if (!skipProvisionalLoadClearedEvent)
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ProvisionalLoadCleared);
+ }
+
+ get id()
+ {
+ return this._id;
+ }
+
+ get loaderIdentifier()
+ {
+ return this._loaderIdentifier;
+ }
+
+ get provisionalLoaderIdentifier()
+ {
+ return this._provisionalLoaderIdentifier;
+ }
+
+ get name()
+ {
+ return this._name;
+ }
+
+ get securityOrigin()
+ {
+ return this._securityOrigin;
+ }
+
+ get url()
+ {
+ return this._mainResource._url;
+ }
+
+ get domTree()
+ {
+ if (!this._domTree)
+ this._domTree = new WebInspector.DOMTree(this);
+ return this._domTree;
+ }
+
+ get pageExecutionContext()
+ {
+ return this._executionContextList.pageExecutionContext;
+ }
+
+ get executionContextList()
+ {
+ return this._executionContextList;
+ }
+
+ clearExecutionContexts(committingProvisionalLoad)
+ {
+ if (this._executionContextList.contexts.length) {
+ let contexts = this._executionContextList.contexts.slice();
+ this._executionContextList.clear();
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ExecutionContextsCleared, {committingProvisionalLoad: !!committingProvisionalLoad, contexts});
+ }
+ }
+
+ addExecutionContext(context)
+ {
+ var changedPageContext = this._executionContextList.add(context);
+
+ if (changedPageContext)
+ this.dispatchEventToListeners(WebInspector.Frame.Event.PageExecutionContextChanged);
+ }
+
+ get mainResource()
+ {
+ return this._mainResource;
+ }
+
+ get provisionalMainResource()
+ {
+ return this._provisionalMainResource;
+ }
+
+ get parentFrame()
+ {
+ return this._parentFrame;
+ }
+
+ get domContentReadyEventTimestamp()
+ {
+ return this._domContentReadyEventTimestamp;
+ }
+
+ get loadEventTimestamp()
+ {
+ return this._loadEventTimestamp;
+ }
+
+ isMainFrame()
+ {
+ return this._isMainFrame;
+ }
+
+ markAsMainFrame()
+ {
+ this._isMainFrame = true;
+ }
+
+ unmarkAsMainFrame()
+ {
+ this._isMainFrame = false;
+ }
+
+ markDOMContentReadyEvent(timestamp)
+ {
+ this._domContentReadyEventTimestamp = timestamp || NaN;
+ }
+
+ markLoadEvent(timestamp)
+ {
+ this._loadEventTimestamp = timestamp || NaN;
+ }
+
+ isDetached()
+ {
+ var frame = this;
+ while (frame) {
+ if (frame.isMainFrame())
+ return false;
+ frame = frame.parentFrame;
+ }
+
+ return true;
+ }
+
+ childFrameForIdentifier(frameId)
+ {
+ return this._childFrameIdentifierMap.get(frameId) || null;
+ }
+
+ addChildFrame(frame)
+ {
+ console.assert(frame instanceof WebInspector.Frame);
+ if (!(frame instanceof WebInspector.Frame))
+ return;
+
+ if (frame._parentFrame === this)
+ return;
+
+ if (frame._parentFrame)
+ frame._parentFrame.removeChildFrame(frame);
+
+ this._childFrameCollection.add(frame);
+ this._childFrameIdentifierMap.set(frame._id, frame);
+
+ frame._parentFrame = this;
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ChildFrameWasAdded, {childFrame: frame});
+ }
+
+ removeChildFrame(frameOrFrameId)
+ {
+ console.assert(frameOrFrameId);
+
+ let childFrameId = frameOrFrameId;
+ if (childFrameId instanceof WebInspector.Frame)
+ childFrameId = frameOrFrameId._id;
+
+ // Fetch the frame by id even if we were passed a WebInspector.Frame.
+ // We do this incase the WebInspector.Frame is a new object that isn't
+ // in _childFrameCollection, but the id is a valid child frame.
+ let childFrame = this.childFrameForIdentifier(childFrameId);
+ console.assert(childFrame instanceof WebInspector.Frame);
+ if (!(childFrame instanceof WebInspector.Frame))
+ return;
+
+ console.assert(childFrame.parentFrame === this);
+
+ this._childFrameCollection.remove(childFrame);
+ this._childFrameIdentifierMap.delete(childFrame._id);
+
+ childFrame._detachFromParentFrame();
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ChildFrameWasRemoved, {childFrame});
+ }
+
+ removeAllChildFrames()
+ {
+ this._detachFromParentFrame();
+
+ for (let childFrame of this._childFrameCollection.items)
+ childFrame.removeAllChildFrames();
+
+ this._childFrameCollection.clear();
+ this._childFrameIdentifierMap.clear();
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.AllChildFramesRemoved);
+ }
+
+ resourceForURL(url, recursivelySearchChildFrames)
+ {
+ var resource = this._resourceCollection.resourceForURL(url);
+ if (resource)
+ return resource;
+
+ // Check the main resources of the child frames for the requested URL.
+ for (let childFrame of this._childFrameCollection.items) {
+ resource = childFrame.mainResource;
+ if (resource.url === url)
+ return resource;
+ }
+
+ if (!recursivelySearchChildFrames)
+ return null;
+
+ // Recursively search resources of child frames.
+ for (let childFrame of this._childFrameCollection.items) {
+ resource = childFrame.resourceForURL(url, true);
+ if (resource)
+ return resource;
+ }
+
+ return null;
+ }
+
+ resourceCollectionForType(type)
+ {
+ return this._resourceCollection.resourceCollectionForType(type);
+ }
+
+ addResource(resource)
+ {
+ console.assert(resource instanceof WebInspector.Resource);
+ if (!(resource instanceof WebInspector.Resource))
+ return;
+
+ if (resource.parentFrame === this)
+ return;
+
+ if (resource.parentFrame)
+ resource.parentFrame.remove(resource);
+
+ this._associateWithResource(resource);
+
+ if (this._isProvisionalResource(resource)) {
+ this._provisionalResourceCollection.add(resource);
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ProvisionalResourceWasAdded, {resource});
+ } else {
+ this._resourceCollection.add(resource);
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ResourceWasAdded, {resource});
+ }
+ }
+
+ removeResource(resource)
+ {
+ // This does not remove provisional resources.
+
+ this._resourceCollection.remove(resource);
+
+ this._disassociateWithResource(resource);
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ResourceWasRemoved, {resource});
+ }
+
+ removeAllResources()
+ {
+ // This does not remove provisional resources, use clearProvisionalLoad for that.
+
+ let resources = this._resourceCollection.items;
+ if (!resources.size)
+ return;
+
+ for (let resource of resources)
+ this._disassociateWithResource(resource);
+
+ this._resourceCollection.clear();
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.AllResourcesRemoved);
+ }
+
+ addExtraScript(script)
+ {
+ this._extraScriptCollection.add(script);
+
+ this.dispatchEventToListeners(WebInspector.Frame.Event.ExtraScriptAdded, {script});
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.Frame.MainResourceURLCookieKey] = this.mainResource.url.hash;
+ cookie[WebInspector.Frame.IsMainFrameCookieKey] = this._isMainFrame;
+ }
+
+ // Private
+
+ _detachFromParentFrame()
+ {
+ if (this._domTree) {
+ this._domTree.disconnect();
+ this._domTree = null;
+ }
+
+ this._parentFrame = null;
+ }
+
+ _isProvisionalResource(resource)
+ {
+ return resource.loaderIdentifier && this._provisionalLoaderIdentifier && resource.loaderIdentifier === this._provisionalLoaderIdentifier;
+ }
+
+ _associateWithResource(resource)
+ {
+ console.assert(!resource._parentFrame);
+ if (resource._parentFrame)
+ return;
+
+ resource._parentFrame = this;
+ }
+
+ _disassociateWithResource(resource)
+ {
+ console.assert(resource.parentFrame === this);
+ if (resource.parentFrame !== this)
+ return;
+
+ resource._parentFrame = null;
+ }
+
+ _dispatchMainResourceDidChangeEvent(oldMainResource)
+ {
+ this.dispatchEventToListeners(WebInspector.Frame.Event.MainResourceDidChange, {oldMainResource});
+ }
+};
+
+WebInspector.Frame.Event = {
+ NameDidChange: "frame-name-did-change",
+ SecurityOriginDidChange: "frame-security-origin-did-change",
+ MainResourceDidChange: "frame-main-resource-did-change",
+ ProvisionalLoadStarted: "frame-provisional-load-started",
+ ProvisionalLoadCommitted: "frame-provisional-load-committed",
+ ProvisionalLoadCleared: "frame-provisional-load-cleared",
+ ProvisionalResourceWasAdded: "frame-provisional-resource-was-added",
+ ResourceWasAdded: "frame-resource-was-added",
+ ResourceWasRemoved: "frame-resource-was-removed",
+ AllResourcesRemoved: "frame-all-resources-removed",
+ ExtraScriptAdded: "frame-extra-script-added",
+ ChildFrameWasAdded: "frame-child-frame-was-added",
+ ChildFrameWasRemoved: "frame-child-frame-was-removed",
+ AllChildFramesRemoved: "frame-all-child-frames-removed",
+ PageExecutionContextChanged: "frame-page-execution-context-changed",
+ ExecutionContextsCleared: "frame-execution-contexts-cleared"
+};
+
+WebInspector.Frame.TypeIdentifier = "Frame";
+WebInspector.Frame.MainResourceURLCookieKey = "frame-main-resource-url";
+WebInspector.Frame.IsMainFrameCookieKey = "frame-is-main-frame";
diff --git a/Source/WebInspectorUI/UserInterface/Models/GarbageCollection.js b/Source/WebInspectorUI/UserInterface/Models/GarbageCollection.js
new file mode 100644
index 000000000..84a3f0b31
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/GarbageCollection.js
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.GarbageCollection = class GarbageCollection extends WebInspector.Object
+{
+ constructor(type, startTime, endTime)
+ {
+ super();
+
+ console.assert(endTime >= startTime);
+
+ this._type = type;
+ this._startTime = startTime;
+ this._endTime = endTime;
+ }
+
+ // Static
+
+ static fromPayload(payload)
+ {
+ let type = WebInspector.GarbageCollection.Type.Full;
+ if (payload.type === HeapAgent.GarbageCollectionType.Partial)
+ type = WebInspector.GarbageCollection.Type.Partial;
+
+ return new WebInspector.GarbageCollection(type, payload.startTime, payload.endTime);
+ }
+
+ // Public
+
+ get type() { return this._type; }
+ get startTime() { return this._startTime; }
+ get endTime() { return this._endTime; }
+
+ get duration()
+ {
+ return this._endTime - this._startTime;
+ }
+};
+
+WebInspector.GarbageCollection.Type = {
+ Partial: Symbol("Partial"),
+ Full: Symbol("Full")
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Geometry.js b/Source/WebInspectorUI/UserInterface/Models/Geometry.js
new file mode 100644
index 000000000..2fde0a558
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Geometry.js
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Point = class Point
+{
+ constructor(x, y)
+ {
+ this.x = x || 0;
+ this.y = y || 0;
+ }
+
+ // Static
+
+ static fromEvent(event)
+ {
+ return new WebInspector.Point(event.pageX, event.pageY);
+ }
+
+ static fromEventInElement(event, element)
+ {
+ var wkPoint = window.webkitConvertPointFromPageToNode(element, new WebKitPoint(event.pageX, event.pageY));
+ return new WebInspector.Point(wkPoint.x, wkPoint.y);
+ }
+
+ // Public
+
+ toString()
+ {
+ return "WebInspector.Point[" + this.x + "," + this.y + "]";
+ }
+
+ copy()
+ {
+ return new WebInspector.Point(this.x, this.y);
+ }
+
+ equals(anotherPoint)
+ {
+ return this.x === anotherPoint.x && this.y === anotherPoint.y;
+ }
+
+ distance(anotherPoint)
+ {
+ var dx = anotherPoint.x - this.x;
+ var dy = anotherPoint.y - this.y;
+ return Math.sqrt(dx * dx, dy * dy);
+ }
+};
+
+WebInspector.Size = class Size
+{
+ constructor(width, height)
+ {
+ this.width = width || 0;
+ this.height = height || 0;
+ }
+
+ // Public
+
+ toString()
+ {
+ return "WebInspector.Size[" + this.width + "," + this.height + "]";
+ }
+
+ copy()
+ {
+ return new WebInspector.Size(this.width, this.height);
+ }
+
+ equals(anotherSize)
+ {
+ return this.width === anotherSize.width && this.height === anotherSize.height;
+ }
+};
+
+WebInspector.Size.ZERO_SIZE = new WebInspector.Size(0, 0);
+
+
+WebInspector.Rect = class Rect
+{
+ constructor(x, y, width, height)
+ {
+ this.origin = new WebInspector.Point(x || 0, y || 0);
+ this.size = new WebInspector.Size(width || 0, height || 0);
+ }
+
+ // Static
+
+ static rectFromClientRect(clientRect)
+ {
+ return new WebInspector.Rect(clientRect.left, clientRect.top, clientRect.width, clientRect.height);
+ }
+
+ static unionOfRects(rects)
+ {
+ var union = rects[0];
+ for (var i = 1; i < rects.length; ++i)
+ union = union.unionWithRect(rects[i]);
+ return union;
+ }
+
+ // Public
+
+ toString()
+ {
+ return "WebInspector.Rect[" + [this.origin.x, this.origin.y, this.size.width, this.size.height].join(", ") + "]";
+ }
+
+ copy()
+ {
+ return new WebInspector.Rect(this.origin.x, this.origin.y, this.size.width, this.size.height);
+ }
+
+ equals(anotherRect)
+ {
+ return this.origin.equals(anotherRect.origin) && this.size.equals(anotherRect.size);
+ }
+
+ inset(insets)
+ {
+ return new WebInspector.Rect(
+ this.origin.x + insets.left,
+ this.origin.y + insets.top,
+ this.size.width - insets.left - insets.right,
+ this.size.height - insets.top - insets.bottom
+ );
+ }
+
+ pad(padding)
+ {
+ return new WebInspector.Rect(
+ this.origin.x - padding,
+ this.origin.y - padding,
+ this.size.width + padding * 2,
+ this.size.height + padding * 2
+ );
+ }
+
+ minX()
+ {
+ return this.origin.x;
+ }
+
+ minY()
+ {
+ return this.origin.y;
+ }
+
+ midX()
+ {
+ return this.origin.x + (this.size.width / 2);
+ }
+
+ midY()
+ {
+ return this.origin.y + (this.size.height / 2);
+ }
+
+ maxX()
+ {
+ return this.origin.x + this.size.width;
+ }
+
+ maxY()
+ {
+ return this.origin.y + this.size.height;
+ }
+
+ intersectionWithRect(rect)
+ {
+ var x1 = Math.max(this.minX(), rect.minX());
+ var x2 = Math.min(this.maxX(), rect.maxX());
+ if (x1 > x2)
+ return WebInspector.Rect.ZERO_RECT;
+ var intersection = new WebInspector.Rect;
+ intersection.origin.x = x1;
+ intersection.size.width = x2 - x1;
+ var y1 = Math.max(this.minY(), rect.minY());
+ var y2 = Math.min(this.maxY(), rect.maxY());
+ if (y1 > y2)
+ return WebInspector.Rect.ZERO_RECT;
+ intersection.origin.y = y1;
+ intersection.size.height = y2 - y1;
+ return intersection;
+ }
+
+ unionWithRect(rect)
+ {
+ var x = Math.min(this.minX(), rect.minX());
+ var y = Math.min(this.minY(), rect.minY());
+ var width = Math.max(this.maxX(), rect.maxX()) - x;
+ var height = Math.max(this.maxY(), rect.maxY()) - y;
+ return new WebInspector.Rect(x, y, width, height);
+ }
+
+ round()
+ {
+ return new WebInspector.Rect(
+ Math.floor(this.origin.x),
+ Math.floor(this.origin.y),
+ Math.ceil(this.size.width),
+ Math.ceil(this.size.height)
+ );
+ }
+};
+
+WebInspector.Rect.ZERO_RECT = new WebInspector.Rect(0, 0, 0, 0);
+
+
+WebInspector.EdgeInsets = class EdgeInsets
+{
+ constructor(top, right, bottom, left)
+ {
+ console.assert(arguments.length === 1 || arguments.length === 4);
+
+ if (arguments.length === 1) {
+ this.top = top;
+ this.right = top;
+ this.bottom = top;
+ this.left = top;
+ } else if (arguments.length === 4) {
+ this.top = top;
+ this.right = right;
+ this.bottom = bottom;
+ this.left = left;
+ }
+ }
+
+ // Public
+
+ equals(anotherInset)
+ {
+ return this.top === anotherInset.top && this.right === anotherInset.right
+ && this.bottom === anotherInset.bottom && this.left === anotherInset.left;
+ }
+
+ copy()
+ {
+ return new WebInspector.EdgeInsets(this.top, this.right, this.bottom, this.left);
+ }
+};
+
+WebInspector.RectEdge = {
+ MIN_X: 0,
+ MIN_Y: 1,
+ MAX_X: 2,
+ MAX_Y: 3
+};
+
+WebInspector.Quad = class Quad
+{
+ constructor(quad)
+ {
+ this.points = [
+ new WebInspector.Point(quad[0], quad[1]), // top left
+ new WebInspector.Point(quad[2], quad[3]), // top right
+ new WebInspector.Point(quad[4], quad[5]), // bottom right
+ new WebInspector.Point(quad[6], quad[7]) // bottom left
+ ];
+
+ this.width = Math.round(Math.sqrt(Math.pow(quad[0] - quad[2], 2) + Math.pow(quad[1] - quad[3], 2)));
+ this.height = Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
+ }
+
+ // Public
+
+ toProtocol()
+ {
+ return [
+ this.points[0].x, this.points[0].y,
+ this.points[1].x, this.points[1].y,
+ this.points[2].x, this.points[2].y,
+ this.points[3].x, this.points[3].y
+ ];
+ }
+};
+
+WebInspector.Polygon = class Polygon
+{
+ constructor(points)
+ {
+ this.points = points;
+ }
+
+ // Public
+
+ bounds()
+ {
+ var minX = Number.MAX_VALUE;
+ var minY = Number.MAX_VALUE;
+ var maxX = -Number.MAX_VALUE;
+ var maxY = -Number.MAX_VALUE;
+ for (var point of this.points) {
+ minX = Math.min(minX, point.x);
+ maxX = Math.max(maxX, point.x);
+ minY = Math.min(minY, point.y);
+ maxY = Math.max(maxY, point.y);
+ }
+ return new WebInspector.Rect(minX, minY, maxX - minX, maxY - minY);
+ }
+};
+
+WebInspector.CubicBezier = class CubicBezier
+{
+ constructor(x1, y1, x2, y2)
+ {
+ this._inPoint = new WebInspector.Point(x1, y1);
+ this._outPoint = new WebInspector.Point(x2, y2);
+
+ // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
+ this._curveInfo = {
+ x: {c: 3.0 * x1},
+ y: {c: 3.0 * y1}
+ };
+
+ this._curveInfo.x.b = 3.0 * (x2 - x1) - this._curveInfo.x.c;
+ this._curveInfo.x.a = 1.0 - this._curveInfo.x.c - this._curveInfo.x.b;
+
+ this._curveInfo.y.b = 3.0 * (y2 - y1) - this._curveInfo.y.c;
+ this._curveInfo.y.a = 1.0 - this._curveInfo.y.c - this._curveInfo.y.b;
+ }
+
+ // Static
+
+ static fromCoordinates(coordinates)
+ {
+ if (!coordinates || coordinates.length < 4)
+ return null;
+
+ coordinates = coordinates.map(Number);
+ if (coordinates.includes(NaN))
+ return null;
+
+ return new WebInspector.CubicBezier(coordinates[0], coordinates[1], coordinates[2], coordinates[3]);
+ }
+
+ static fromString(text)
+ {
+ if (!text || !text.length)
+ return null;
+
+ var trimmedText = text.toLowerCase().replace(/\s/g, "");
+ if (!trimmedText.length)
+ return null;
+
+ if (Object.keys(WebInspector.CubicBezier.keywordValues).includes(trimmedText))
+ return WebInspector.CubicBezier.fromCoordinates(WebInspector.CubicBezier.keywordValues[trimmedText]);
+
+ var matches = trimmedText.match(/^cubic-bezier\(([-\d.]+),([-\d.]+),([-\d.]+),([-\d.]+)\)$/);
+ if (!matches)
+ return null;
+
+ matches.splice(0, 1);
+ return WebInspector.CubicBezier.fromCoordinates(matches);
+ }
+
+ // Public
+
+ get inPoint()
+ {
+ return this._inPoint;
+ }
+
+ get outPoint()
+ {
+ return this._outPoint;
+ }
+
+ copy()
+ {
+ return new WebInspector.CubicBezier(this._inPoint.x, this._inPoint.y, this._outPoint.x, this._outPoint.y);
+ }
+
+ toString()
+ {
+ var values = [this._inPoint.x, this._inPoint.y, this._outPoint.x, this._outPoint.y];
+ for (var key in WebInspector.CubicBezier.keywordValues) {
+ if (Array.shallowEqual(WebInspector.CubicBezier.keywordValues[key], values))
+ return key;
+ }
+
+ return "cubic-bezier(" + values.join(", ") + ")";
+ }
+
+ solve(x, epsilon)
+ {
+ return this._sampleCurveY(this._solveCurveX(x, epsilon));
+ }
+
+ // Private
+
+ _sampleCurveX(t)
+ {
+ // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
+ return ((this._curveInfo.x.a * t + this._curveInfo.x.b) * t + this._curveInfo.x.c) * t;
+ }
+
+ _sampleCurveY(t)
+ {
+ return ((this._curveInfo.y.a * t + this._curveInfo.y.b) * t + this._curveInfo.y.c) * t;
+ }
+
+ _sampleCurveDerivativeX(t)
+ {
+ return (3.0 * this._curveInfo.x.a * t + 2.0 * this._curveInfo.x.b) * t + this._curveInfo.x.c;
+ }
+
+ // Given an x value, find a parametric value it came from.
+ _solveCurveX(x, epsilon)
+ {
+ var t0, t1, t2, x2, d2, i;
+
+ // First try a few iterations of Newton's method -- normally very fast.
+ for (t2 = x, i = 0; i < 8; i++) {
+ x2 = this._sampleCurveX(t2) - x;
+ if (Math.abs(x2) < epsilon)
+ return t2;
+ d2 = this._sampleCurveDerivativeX(t2);
+ if (Math.abs(d2) < 1e-6)
+ break;
+ t2 = t2 - x2 / d2;
+ }
+
+ // Fall back to the bisection method for reliability.
+ t0 = 0.0;
+ t1 = 1.0;
+ t2 = x;
+
+ if (t2 < t0)
+ return t0;
+ if (t2 > t1)
+ return t1;
+
+ while (t0 < t1) {
+ x2 = this._sampleCurveX(t2);
+ if (Math.abs(x2 - x) < epsilon)
+ return t2;
+ if (x > x2)
+ t0 = t2;
+ else
+ t1 = t2;
+ t2 = (t1 - t0) * 0.5 + t0;
+ }
+
+ // Failure.
+ return t2;
+ }
+};
+
+WebInspector.CubicBezier.keywordValues = {
+ "ease": [0.25, 0.1, 0.25, 1],
+ "ease-in": [0.42, 0, 1, 1],
+ "ease-out": [0, 0, 0.58, 1],
+ "ease-in-out": [0.42, 0, 0.58, 1],
+ "linear": [0, 0, 1, 1]
+};
+
+WebInspector.Spring = class Spring
+{
+ constructor(mass, stiffness, damping, initialVelocity)
+ {
+ this.mass = Math.max(1, mass);
+ this.stiffness = Math.max(1, stiffness);
+ this.damping = Math.max(0, damping);
+ this.initialVelocity = initialVelocity;
+ }
+
+ // Static
+
+ static fromValues(values)
+ {
+ if (!values || values.length < 4)
+ return null;
+
+ values = values.map(Number);
+ if (values.includes(NaN))
+ return null;
+
+ return new WebInspector.Spring(...values);
+ }
+
+ static fromString(text)
+ {
+ if (!text || !text.length)
+ return null;
+
+ let trimmedText = text.toLowerCase().trim();
+ if (!trimmedText.length)
+ return null;
+
+ let matches = trimmedText.match(/^spring\(([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([-\d.]+)\)$/);
+ if (!matches)
+ return null;
+
+ return WebInspector.Spring.fromValues(matches.slice(1));
+ }
+
+ // Public
+
+ copy()
+ {
+ return new WebInspector.Spring(this.mass, this.stiffness, this.damping, this.initialVelocity);
+ }
+
+ toString()
+ {
+ return `spring(${this.mass} ${this.stiffness} ${this.damping} ${this.initialVelocity})`;
+ }
+
+ solve(t)
+ {
+ let w0 = Math.sqrt(this.stiffness / this.mass);
+ let zeta = this.damping / (2 * Math.sqrt(this.stiffness * this.mass));
+
+ let wd = 0;
+ let A = 1;
+ let B = -this.initialVelocity + w0;
+ if (zeta < 1) {
+ // Under-damped.
+ wd = w0 * Math.sqrt(1 - zeta * zeta);
+ A = 1;
+ B = (zeta * w0 + -this.initialVelocity) / wd;
+ }
+
+ if (zeta < 1) // Under-damped
+ t = Math.exp(-t * zeta * w0) * (A * Math.cos(wd * t) + B * Math.sin(wd * t));
+ else // Critically damped (ignoring over-damped case).
+ t = (A + B * t) * Math.exp(-t * w0);
+
+ return 1 - t; // Map range from [1..0] to [0..1].
+ }
+
+ calculateDuration(epsilon)
+ {
+ epsilon = epsilon || 0.0001;
+ let t = 0;
+ let current = 0;
+ let minimum = Number.POSITIVE_INFINITY;
+ while (current >= epsilon || minimum >= epsilon) {
+ current = Math.abs(1 - this.solve(t)); // Undo the range mapping
+ if (minimum < epsilon && current >= epsilon)
+ minimum = Number.POSITIVE_INFINITY; // Spring reversed direction
+ else if (current < minimum)
+ minimum = current;
+ t += 0.1;
+ }
+ return t;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Gradient.js b/Source/WebInspectorUI/UserInterface/Models/Gradient.js
new file mode 100644
index 000000000..f9b1c238c
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Gradient.js
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Gradient = class Gradient
+{
+ constructor(type, stops)
+ {
+ this.type = type;
+ this.stops = stops;
+ }
+
+ // Static
+
+ static fromString(cssString)
+ {
+ var type;
+ var openingParenthesisIndex = cssString.indexOf("(");
+ var typeString = cssString.substring(0, openingParenthesisIndex);
+ if (typeString.indexOf(WebInspector.Gradient.Types.Linear) !== -1)
+ type = WebInspector.Gradient.Types.Linear;
+ else if (typeString.indexOf(WebInspector.Gradient.Types.Radial) !== -1)
+ type = WebInspector.Gradient.Types.Radial;
+ else
+ return null;
+
+ var components = [];
+ var currentParams = [];
+ var currentParam = "";
+ var openParentheses = 0;
+ var ch = openingParenthesisIndex + 1;
+ var c = null;
+ while (c = cssString[ch]) {
+ if (c === "(")
+ openParentheses++;
+ if (c === ")")
+ openParentheses--;
+
+ var isComma = c === ",";
+ var isSpace = /\s/.test(c);
+
+ if (openParentheses === 0) {
+ if (isSpace) {
+ if (currentParam !== "")
+ currentParams.push(currentParam);
+ currentParam = "";
+ } else if (isComma) {
+ currentParams.push(currentParam);
+ components.push(currentParams);
+ currentParams = [];
+ currentParam = "";
+ }
+ }
+
+ if (openParentheses === -1) {
+ currentParams.push(currentParam);
+ components.push(currentParams);
+ break;
+ }
+
+ if (openParentheses > 0 || (!isComma && !isSpace))
+ currentParam += c;
+
+ ch++;
+ }
+
+ if (openParentheses !== -1)
+ return null;
+
+ var gradient;
+ if (type === WebInspector.Gradient.Types.Linear)
+ gradient = WebInspector.LinearGradient.fromComponents(components);
+ else
+ gradient = WebInspector.RadialGradient.fromComponents(components);
+
+ if (gradient)
+ gradient.repeats = typeString.startsWith("repeating");
+
+ return gradient;
+ }
+
+ static stopsWithComponents(components)
+ {
+ // FIXME: handle lengths.
+ var stops = components.map(function(component) {
+ while (component.length) {
+ var color = WebInspector.Color.fromString(component.shift());
+ if (!color)
+ continue;
+
+ var stop = {color};
+ if (component.length && component[0].substr(-1) === "%")
+ stop.offset = parseFloat(component.shift()) / 100;
+ return stop;
+ }
+ });
+
+ if (!stops.length)
+ return null;
+
+ for (var i = 0, count = stops.length; i < count; ++i) {
+ var stop = stops[i];
+
+ // If one of the stops failed to parse, then this is not a valid
+ // set of components for a gradient. So the whole thing is invalid.
+ if (!stop)
+ return null;
+
+ if (!stop.offset)
+ stop.offset = i / (count - 1);
+ }
+
+ return stops;
+ }
+
+ // Public
+
+ stringFromStops(stops)
+ {
+ var count = stops.length - 1;
+ return stops.map(function(stop, index) {
+ var str = stop.color;
+ if (stop.offset !== index / count)
+ str += " " + Math.round(stop.offset * 10000) / 100 + "%";
+ return str;
+ }).join(", ");
+ }
+
+ // Public
+
+ copy()
+ {
+ // Implemented by subclasses.
+ }
+
+ toString()
+ {
+ // Implemented by subclasses.
+ }
+};
+
+WebInspector.Gradient.Types = {
+ Linear: "linear-gradient",
+ Radial: "radial-gradient"
+};
+
+WebInspector.LinearGradient = class LinearGradient extends WebInspector.Gradient
+{
+ constructor(angle, stops)
+ {
+ super(WebInspector.Gradient.Types.Linear, stops);
+ this._angle = angle;
+ }
+
+ // Static
+
+ static fromComponents(components)
+ {
+ let angle = {value: 180, units: WebInspector.LinearGradient.AngleUnits.DEG};
+
+ if (components[0].length === 1 && !WebInspector.Color.fromString(components[0][0])) {
+ let match = components[0][0].match(/([-\d\.]+)(\w+)/);
+ if (!match || !Object.values(WebInspector.LinearGradient.AngleUnits).includes(match[2]))
+ return null;
+
+ angle.value = parseFloat(match[1]);
+ angle.units = match[2];
+
+ components.shift();
+ } else if (components[0][0] === "to") {
+ components[0].shift();
+ switch (components[0].sort().join(" ")) {
+ case "top":
+ angle.value = 0;
+ break;
+ case "right top":
+ angle.value = 45;
+ break;
+ case "right":
+ angle.value = 90;
+ break;
+ case "bottom right":
+ angle.value = 135;
+ break;
+ case "bottom":
+ angle.value = 180;
+ break;
+ case "bottom left":
+ angle.value = 225;
+ break;
+ case "left":
+ angle.value = 270;
+ break;
+ case "left top":
+ angle.value = 315;
+ break;
+ default:
+ return null;
+ }
+
+ components.shift();
+ } else if (components[0].length !== 1 && !WebInspector.Color.fromString(components[0][0])) {
+ // If the first component is not a color, then we're dealing with a
+ // legacy linear gradient format that we don't support.
+ return null;
+ }
+
+ var stops = WebInspector.Gradient.stopsWithComponents(components);
+ if (!stops)
+ return null;
+
+ return new WebInspector.LinearGradient(angle, stops);
+ }
+
+ // Public
+
+ set angleValue(value) { this._angle.value = value; }
+
+ get angleValue()
+ {
+ return this._angle.value.maxDecimals(2);
+ }
+
+ set angleUnits(units)
+ {
+ if (units === this._angle.units)
+ return;
+
+ this._angle.value = this._angleValueForUnits(units);
+ this._angle.units = units;
+ }
+
+ get angleUnits() { return this._angle.units; }
+
+ copy()
+ {
+ return new WebInspector.LinearGradient(this._angle, this.stops.concat());
+ }
+
+ toString()
+ {
+ let str = "";
+
+ let deg = this._angleValueForUnits(WebInspector.LinearGradient.AngleUnits.DEG);
+ if (deg === 0)
+ str += "to top";
+ else if (deg === 45)
+ str += "to top right";
+ else if (deg === 90)
+ str += "to right";
+ else if (deg === 135)
+ str += "to bottom right";
+ else if (deg === 225)
+ str += "to bottom left";
+ else if (deg === 270)
+ str += "to left";
+ else if (deg === 315)
+ str += "to top left";
+ else if (deg !== 180)
+ str += this.angleValue + this.angleUnits;
+
+ if (str !== "")
+ str += ", ";
+
+ str += this.stringFromStops(this.stops);
+
+ return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
+ }
+
+ // Private
+
+ _angleValueForUnits(units)
+ {
+ if (units === this._angle.units)
+ return this._angle.value;
+
+ let deg = 0;
+
+ switch (this._angle.units) {
+ case WebInspector.LinearGradient.AngleUnits.DEG:
+ deg = this._angle.value;
+ break;
+
+ case WebInspector.LinearGradient.AngleUnits.RAD:
+ deg = this._angle.value * 180 / Math.PI;
+ break;
+
+ case WebInspector.LinearGradient.AngleUnits.GRAD:
+ deg = this._angle.value / 400 * 360;
+ break;
+
+ case WebInspector.LinearGradient.AngleUnits.TURN:
+ deg = this._angle.value * 360;
+ break;
+
+ default:
+ WebInspector.reportInternalError(`Unknown angle units "${this._angle.units}"`);
+ return 0;
+ }
+
+ let value = 0;
+
+ switch (units) {
+ case WebInspector.LinearGradient.AngleUnits.DEG:
+ value = deg;
+ break;
+
+ case WebInspector.LinearGradient.AngleUnits.RAD:
+ value = deg * Math.PI / 180;
+ break;
+
+ case WebInspector.LinearGradient.AngleUnits.GRAD:
+ value = deg / 360 * 400;
+ break;
+
+ case WebInspector.LinearGradient.AngleUnits.TURN:
+ value = deg / 360;
+ break;
+ }
+
+ return value;
+ }
+};
+
+WebInspector.LinearGradient.AngleUnits = {
+ DEG: "deg",
+ RAD: "rad",
+ GRAD: "grad",
+ TURN: "turn",
+};
+
+WebInspector.RadialGradient = class RadialGradient extends WebInspector.Gradient
+{
+ constructor(sizing, stops)
+ {
+ super(WebInspector.Gradient.Types.Radial, stops);
+ this.sizing = sizing;
+ }
+
+ // Static
+
+ static fromComponents(components)
+ {
+ var sizing = !WebInspector.Color.fromString(components[0].join(" ")) ? components.shift().join(" ") : "";
+
+ var stops = WebInspector.Gradient.stopsWithComponents(components);
+ if (!stops)
+ return null;
+
+ return new WebInspector.RadialGradient(sizing, stops);
+ }
+
+ // Public
+
+ copy()
+ {
+ return new WebInspector.RadialGradient(this.sizing, this.stops.concat());
+ }
+
+ toString()
+ {
+ var str = this.sizing;
+
+ if (str !== "")
+ str += ", ";
+
+ str += this.stringFromStops(this.stops);
+
+ return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js
new file mode 100644
index 000000000..f35c18408
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.HeapAllocationsInstrument = class HeapAllocationsInstrument extends WebInspector.Instrument
+{
+ constructor()
+ {
+ super();
+
+ console.assert(WebInspector.HeapAllocationsInstrument.supported());
+
+ this._snapshotIntervalIdentifier = undefined;
+ }
+
+ // Static
+
+ static supported()
+ {
+ // COMPATIBILITY (iOS 9): HeapAgent did not exist.
+ return window.HeapAgent;
+ }
+
+ // Protected
+
+ get timelineRecordType()
+ {
+ return WebInspector.TimelineRecord.Type.HeapAllocations;
+ }
+
+ startInstrumentation(initiatedByBackend)
+ {
+ // FIXME: Include a "track allocations" option for this instrument.
+ // FIXME: Include a periodic snapshot interval option for this instrument.
+
+ if (!initiatedByBackend)
+ HeapAgent.startTracking();
+
+ // Periodic snapshots.
+ const snapshotInterval = 10000;
+ this._snapshotIntervalIdentifier = setInterval(this._takeHeapSnapshot.bind(this), snapshotInterval);
+ }
+
+ stopInstrumentation(initiatedByBackend)
+ {
+ if (!initiatedByBackend)
+ HeapAgent.stopTracking();
+
+ window.clearInterval(this._snapshotIntervalIdentifier);
+ this._snapshotIntervalIdentifier = undefined;
+ }
+
+ // Private
+
+ _takeHeapSnapshot()
+ {
+ HeapAgent.snapshot(function(error, timestamp, snapshotStringData) {
+ let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+ workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => {
+ let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+ WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
+ });
+ });
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js
new file mode 100644
index 000000000..853fe3ecd
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.HeapAllocationsTimelineRecord = class HeapAllocationsTimelineRecord extends WebInspector.TimelineRecord
+{
+ constructor(timestamp, heapSnapshot)
+ {
+ super(WebInspector.TimelineRecord.Type.HeapAllocations, timestamp, timestamp);
+
+ console.assert(typeof timestamp === "number");
+ console.assert(heapSnapshot instanceof WebInspector.HeapSnapshotProxy);
+
+ this._timestamp = timestamp;
+ this._heapSnapshot = heapSnapshot;
+ }
+
+ // Public
+
+ get timestamp() { return this._timestamp; }
+ get heapSnapshot() { return this._heapSnapshot; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js b/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js
new file mode 100644
index 000000000..67bfa8ab3
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.HeapSnapshotRootPath = class HeapSnapshotRootPath extends WebInspector.Object
+{
+ constructor(node, pathComponent, parent, isGlobalScope)
+ {
+ super();
+
+ console.assert(!node || node instanceof WebInspector.HeapSnapshotNodeProxy);
+ console.assert(!pathComponent || typeof pathComponent === "string");
+ console.assert(!parent || parent instanceof WebInspector.HeapSnapshotRootPath);
+
+ this._node = node || null;
+ this._parent = parent || null;
+ this._pathComponent = typeof pathComponent === "string" ? pathComponent : null;
+ this._isGlobalScope = isGlobalScope || false;
+
+ // Become the new root when appended to an empty path.
+ if (this._parent && this._parent.isEmpty())
+ this._parent = null;
+ }
+
+ // Static
+
+ static emptyPath()
+ {
+ return new WebInspector.HeapSnapshotRootPath(null);
+ }
+
+ static pathComponentForIndividualEdge(edge)
+ {
+ switch (edge.type) {
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Internal:
+ return null;
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Index:
+ return "[" + edge.data + "]";
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Property:
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Variable:
+ if (WebInspector.HeapSnapshotRootPath.canPropertyNameBeDotAccess(edge.data))
+ return edge.data;
+ return "[" + doubleQuotedString(edge.data) + "]";
+ }
+ }
+
+ static canPropertyNameBeDotAccess(propertyName)
+ {
+ return /^(?![0-9])\w+$/.test(propertyName);
+ }
+
+ // Public
+
+ get node() { return this._node; }
+ get parent() { return this._parent; }
+ get pathComponent() { return this._pathComponent; }
+
+ get rootNode()
+ {
+ return this._parent ? this._parent.rootNode : this._node;
+ }
+
+ get fullPath()
+ {
+ let components = [];
+ for (let p = this; p && p.pathComponent; p = p.parent)
+ components.push(p.pathComponent);
+ components.reverse();
+ return components.join("");
+ }
+
+ isRoot()
+ {
+ return !this._parent;
+ }
+
+ isEmpty()
+ {
+ return !this._node;
+ }
+
+ isGlobalScope()
+ {
+ return this._isGlobalScope;
+ }
+
+ isPathComponentImpossible()
+ {
+ return this._pathComponent && this._pathComponent.startsWith("@");
+ }
+
+ isFullPathImpossible()
+ {
+ if (this.isEmpty())
+ return true;
+
+ if (this.isPathComponentImpossible())
+ return true;
+
+ if (this._parent)
+ return this._parent.isFullPathImpossible();
+
+ return false;
+ }
+
+ appendInternal(node)
+ {
+ return new WebInspector.HeapSnapshotRootPath(node, WebInspector.HeapSnapshotRootPath.SpecialPathComponent.InternalPropertyName, this);
+ }
+
+ appendArrayIndex(node, index)
+ {
+ let component = "[" + index + "]";
+ return new WebInspector.HeapSnapshotRootPath(node, component, this);
+ }
+
+ appendPropertyName(node, propertyName)
+ {
+ let component = WebInspector.HeapSnapshotRootPath.canPropertyNameBeDotAccess(propertyName) ? "." + propertyName : "[" + doubleQuotedString(propertyName) + "]";
+ return new WebInspector.HeapSnapshotRootPath(node, component, this);
+ }
+
+ appendVariableName(node, variableName)
+ {
+ // Treat as a property of the global object, e.g. "window.foo".
+ if (this._isGlobalScope)
+ return this.appendPropertyName(node, variableName);
+ return new WebInspector.HeapSnapshotRootPath(node, variableName, this);
+ }
+
+ appendGlobalScopeName(node, globalScopeName)
+ {
+ return new WebInspector.HeapSnapshotRootPath(node, globalScopeName, this, true);
+ }
+
+ appendEdge(edge)
+ {
+ console.assert(edge instanceof WebInspector.HeapSnapshotEdgeProxy);
+
+ switch (edge.type) {
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Internal:
+ return this.appendInternal(edge.to);
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Index:
+ return this.appendArrayIndex(edge.to, edge.data);
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Property:
+ return this.appendPropertyName(edge.to, edge.data);
+ case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Variable:
+ return this.appendVariableName(edge.to, edge.data);
+ }
+
+ console.error("Unexpected edge type", edge.type);
+ }
+};
+
+WebInspector.HeapSnapshotRootPath.SpecialPathComponent = {
+ InternalPropertyName: "@internal",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/IndexedDatabase.js b/Source/WebInspectorUI/UserInterface/Models/IndexedDatabase.js
new file mode 100644
index 000000000..f8ac1b54c
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/IndexedDatabase.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.IndexedDatabase = class IndexedDatabase extends WebInspector.Object
+{
+ constructor(name, securityOrigin, version, objectStores)
+ {
+ super();
+
+ this._name = name;
+ this._securityOrigin = securityOrigin;
+ this._host = parseSecurityOrigin(securityOrigin).host;
+ this._version = version;
+ this._objectStores = objectStores || [];
+
+ for (var objectStore of this._objectStores)
+ objectStore.establishRelationship(this);
+ }
+
+ // Public
+
+ get name() { return this._name; }
+ get securityOrigin() { return this._securityOrigin; }
+ get host() { return this._host; }
+ get version() { return this._version; }
+ get objectStores() { return this._objectStores; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.IndexedDatabase.NameCookieKey] = this._name;
+ cookie[WebInspector.IndexedDatabase.HostCookieKey] = this._host;
+ }
+};
+
+WebInspector.IndexedDatabase.TypeIdentifier = "indexed-database";
+WebInspector.IndexedDatabase.NameCookieKey = "indexed-database-name";
+WebInspector.IndexedDatabase.HostCookieKey = "indexed-database-host";
diff --git a/Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStore.js b/Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStore.js
new file mode 100644
index 000000000..91b45b122
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStore.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.IndexedDatabaseObjectStore = class IndexedDatabaseObjectStore extends WebInspector.Object
+{
+ constructor(name, keyPath, autoIncrement, indexes)
+ {
+ super();
+
+ this._name = name;
+ this._keyPath = keyPath;
+ this._autoIncrement = autoIncrement || false;
+ this._indexes = indexes || [];
+ this._parentDatabase = null;
+
+ for (var index of this._indexes)
+ index.establishRelationship(this);
+ }
+
+ // Public
+
+ get name() { return this._name; }
+ get keyPath() { return this._keyPath; }
+ get autoIncrement() { return this._autoIncrement; }
+ get parentDatabase() { return this._parentDatabase; }
+ get indexes() { return this._indexes; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.IndexedDatabaseObjectStore.NameCookieKey] = this._name;
+ cookie[WebInspector.IndexedDatabaseObjectStore.KeyPathCookieKey] = this._keyPath;
+ }
+
+ // Protected
+
+ establishRelationship(parentDatabase)
+ {
+ this._parentDatabase = parentDatabase || null;
+ }
+};
+
+WebInspector.IndexedDatabaseObjectStore.TypeIdentifier = "indexed-database-object-store";
+WebInspector.IndexedDatabaseObjectStore.NameCookieKey = "indexed-database-object-store-name";
+WebInspector.IndexedDatabaseObjectStore.KeyPathCookieKey = "indexed-database-object-store-key-path";
diff --git a/Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStoreIndex.js b/Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStoreIndex.js
new file mode 100644
index 000000000..bdfa572ff
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/IndexedDatabaseObjectStoreIndex.js
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.IndexedDatabaseObjectStoreIndex = class IndexedDatabaseObjectStoreIndex extends WebInspector.Object
+{
+ constructor(name, keyPath, unique, multiEntry)
+ {
+ super();
+
+ this._name = name;
+ this._keyPath = keyPath;
+ this._unique = unique || false;
+ this._multiEntry = multiEntry || false;
+ this._parentObjectStore = null;
+ }
+
+ // Public
+
+ get name() { return this._name; }
+ get keyPath() { return this._keyPath; }
+ get unique() { return this._unique; }
+ get multiEntry() { return this._multiEntry; }
+ get parentObjectStore() { return this._parentObjectStore; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.IndexedDatabaseObjectStoreIndex.NameCookieKey] = this._name;
+ cookie[WebInspector.IndexedDatabaseObjectStoreIndex.KeyPathCookieKey] = this._keyPath;
+ }
+
+ // Protected
+
+ establishRelationship(parentObjectStore)
+ {
+ this._parentObjectStore = parentObjectStore || null;
+ }
+};
+
+WebInspector.IndexedDatabaseObjectStoreIndex.TypeIdentifier = "indexed-database-object-store-index";
+WebInspector.IndexedDatabaseObjectStoreIndex.NameCookieKey = "indexed-database-object-store-index-name";
+WebInspector.IndexedDatabaseObjectStoreIndex.KeyPathCookieKey = "indexed-database-object-store-index-key-path";
diff --git a/Source/WebInspectorUI/UserInterface/Models/Instrument.js b/Source/WebInspectorUI/UserInterface/Models/Instrument.js
new file mode 100644
index 000000000..7659a0bd2
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Instrument.js
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Instrument = class Instrument extends WebInspector.Object
+{
+ // Static
+
+ static createForTimelineType(type)
+ {
+ switch (type) {
+ case WebInspector.TimelineRecord.Type.Network:
+ return new WebInspector.NetworkInstrument;
+ case WebInspector.TimelineRecord.Type.Layout:
+ return new WebInspector.LayoutInstrument;
+ case WebInspector.TimelineRecord.Type.Script:
+ return new WebInspector.ScriptInstrument;
+ case WebInspector.TimelineRecord.Type.RenderingFrame:
+ return new WebInspector.FPSInstrument;
+ case WebInspector.TimelineRecord.Type.Memory:
+ return new WebInspector.MemoryInstrument;
+ case WebInspector.TimelineRecord.Type.HeapAllocations:
+ return new WebInspector.HeapAllocationsInstrument;
+ default:
+ console.error("Unknown TimelineRecord.Type: " + type);
+ return null;
+ }
+ }
+
+ static startLegacyTimelineAgent(initiatedByBackend)
+ {
+ console.assert(window.TimelineAgent, "Attempted to start legacy timeline agent without TimelineAgent.");
+
+ if (WebInspector.Instrument._legacyTimelineAgentStarted)
+ return;
+
+ WebInspector.Instrument._legacyTimelineAgentStarted = true;
+
+ if (initiatedByBackend)
+ return;
+
+ let result = TimelineAgent.start();
+
+ // COMPATIBILITY (iOS 7): recordingStarted event did not exist yet. Start explicitly.
+ if (!TimelineAgent.hasEvent("recordingStarted")) {
+ result.then(function() {
+ WebInspector.timelineManager.capturingStarted();
+ });
+ }
+ }
+
+ static stopLegacyTimelineAgent(initiatedByBackend)
+ {
+ if (!WebInspector.Instrument._legacyTimelineAgentStarted)
+ return;
+
+ WebInspector.Instrument._legacyTimelineAgentStarted = false;
+
+ if (initiatedByBackend)
+ return;
+
+ TimelineAgent.stop();
+ }
+
+ // Protected
+
+ get timelineRecordType()
+ {
+ return null; // Implemented by subclasses.
+ }
+
+ startInstrumentation(initiatedByBackend)
+ {
+ WebInspector.Instrument.startLegacyTimelineAgent(initiatedByBackend);
+ }
+
+ stopInstrumentation(initiatedByBackend)
+ {
+ WebInspector.Instrument.stopLegacyTimelineAgent(initiatedByBackend);
+ }
+};
+
+WebInspector.Instrument._legacyTimelineAgentStarted = false;
diff --git a/Source/WebInspectorUI/UserInterface/Models/IssueMessage.js b/Source/WebInspectorUI/UserInterface/Models/IssueMessage.js
new file mode 100644
index 000000000..43acd7324
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/IssueMessage.js
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.IssueMessage = class IssueMessage extends WebInspector.Object
+{
+ constructor(consoleMessage)
+ {
+ super();
+
+ console.assert(consoleMessage instanceof WebInspector.ConsoleMessage);
+
+ this._consoleMessage = consoleMessage;
+
+ this._text = this._issueText();
+
+ switch (this._consoleMessage.source) {
+ case "javascript":
+ // FIXME: It would be nice if we had this information (the specific type of JavaScript error)
+ // as part of the data passed from WebCore, instead of having to determine it ourselves.
+ var prefixRegex = /^([^:]+): (?:DOM Exception \d+: )?/;
+ var match = prefixRegex.exec(this._text);
+ if (match && match[1] in WebInspector.IssueMessage.Type._prefixTypeMap) {
+ this._type = WebInspector.IssueMessage.Type._prefixTypeMap[match[1]];
+ this._text = this._text.substring(match[0].length);
+ } else
+ this._type = WebInspector.IssueMessage.Type.OtherIssue;
+ break;
+
+ case "css":
+ case "xml":
+ this._type = WebInspector.IssueMessage.Type.PageIssue;
+ break;
+
+ case "network":
+ this._type = WebInspector.IssueMessage.Type.NetworkIssue;
+ break;
+
+ case "security":
+ this._type = WebInspector.IssueMessage.Type.SecurityIssue;
+ break;
+
+ case "console-api":
+ case "storage":
+ case "appcache":
+ case "rendering":
+ case "other":
+ this._type = WebInspector.IssueMessage.Type.OtherIssue;
+ break;
+
+ default:
+ console.error("Unknown issue source:", this._consoleMessage.source);
+ this._type = WebInspector.IssueMessage.Type.OtherIssue;
+ }
+
+ this._sourceCodeLocation = consoleMessage.sourceCodeLocation;
+ if (this._sourceCodeLocation)
+ this._sourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, this._sourceCodeLocationDisplayLocationChanged, this);
+ }
+
+ // Static
+
+ static displayName(type)
+ {
+ switch (type) {
+ case WebInspector.IssueMessage.Type.SemanticIssue:
+ return WebInspector.UIString("Semantic Issue");
+ case WebInspector.IssueMessage.Type.RangeIssue:
+ return WebInspector.UIString("Range Issue");
+ case WebInspector.IssueMessage.Type.ReferenceIssue:
+ return WebInspector.UIString("Reference Issue");
+ case WebInspector.IssueMessage.Type.TypeIssue:
+ return WebInspector.UIString("Type Issue");
+ case WebInspector.IssueMessage.Type.PageIssue:
+ return WebInspector.UIString("Page Issue");
+ case WebInspector.IssueMessage.Type.NetworkIssue:
+ return WebInspector.UIString("Network Issue");
+ case WebInspector.IssueMessage.Type.SecurityIssue:
+ return WebInspector.UIString("Security Issue");
+ case WebInspector.IssueMessage.Type.OtherIssue:
+ return WebInspector.UIString("Other Issue");
+ default:
+ console.error("Unknown issue message type:", type);
+ return WebInspector.UIString("Other Issue");
+ }
+ }
+
+ // Public
+
+ get text() { return this._text; }
+ get type() { return this._type; }
+ get level() { return this._consoleMessage.level; }
+ get source() { return this._consoleMessage.source; }
+ get url() { return this._consoleMessage.url; }
+ get sourceCodeLocation() { return this._sourceCodeLocation; }
+
+ // Protected
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.IssueMessage.URLCookieKey] = this.url;
+ cookie[WebInspector.IssueMessage.LineNumberCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.lineNumber : 0;
+ cookie[WebInspector.IssueMessage.ColumnNumberCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.columnNumber : 0;
+ }
+
+ // Private
+
+ _issueText()
+ {
+ let parameters = this._consoleMessage.parameters;
+ if (!parameters)
+ return this._consoleMessage.messageText;
+
+ if (WebInspector.RemoteObject.type(parameters[0]) !== "string")
+ return this._consoleMessage.messageText;
+
+ function valueFormatter(obj)
+ {
+ return obj.description;
+ }
+
+ let formatters = {};
+ formatters.o = valueFormatter;
+ formatters.s = valueFormatter;
+ formatters.f = valueFormatter;
+ formatters.i = valueFormatter;
+ formatters.d = valueFormatter;
+
+ function append(a, b)
+ {
+ a += b;
+ return a;
+ }
+
+ let result = String.format(parameters[0].description, parameters.slice(1), formatters, "", append);
+ let resultText = result.formattedResult;
+
+ for (let i = 0; i < result.unusedSubstitutions.length; ++i)
+ resultText += " " + result.unusedSubstitutions[i].description;
+
+ return resultText;
+ }
+
+ _sourceCodeLocationDisplayLocationChanged(event)
+ {
+ this.dispatchEventToListeners(WebInspector.IssueMessage.Event.DisplayLocationDidChange, event.data);
+ }
+};
+
+WebInspector.IssueMessage.Level = {
+ Error: "error",
+ Warning: "warning"
+};
+
+WebInspector.IssueMessage.Type = {
+ SemanticIssue: "issue-message-type-semantic-issue",
+ RangeIssue: "issue-message-type-range-issue",
+ ReferenceIssue: "issue-message-type-reference-issue",
+ TypeIssue: "issue-message-type-type-issue",
+ PageIssue: "issue-message-type-page-issue",
+ NetworkIssue: "issue-message-type-network-issue",
+ SecurityIssue: "issue-message-type-security-issue",
+ OtherIssue: "issue-message-type-other-issue"
+};
+
+WebInspector.IssueMessage.TypeIdentifier = "issue-message";
+WebInspector.IssueMessage.URLCookieKey = "issue-message-url";
+WebInspector.IssueMessage.LineNumberCookieKey = "issue-message-line-number";
+WebInspector.IssueMessage.ColumnNumberCookieKey = "issue-message-column-number";
+
+WebInspector.IssueMessage.Event = {
+ LocationDidChange: "issue-message-location-did-change",
+ DisplayLocationDidChange: "issue-message-display-location-did-change"
+};
+
+WebInspector.IssueMessage.Type._prefixTypeMap = {
+ "SyntaxError": WebInspector.IssueMessage.Type.SemanticIssue,
+ "URIError": WebInspector.IssueMessage.Type.SemanticIssue,
+ "EvalError": WebInspector.IssueMessage.Type.SemanticIssue,
+ "INVALID_CHARACTER_ERR": WebInspector.IssueMessage.Type.SemanticIssue,
+ "SYNTAX_ERR": WebInspector.IssueMessage.Type.SemanticIssue,
+
+ "RangeError": WebInspector.IssueMessage.Type.RangeIssue,
+ "INDEX_SIZE_ERR": WebInspector.IssueMessage.Type.RangeIssue,
+ "DOMSTRING_SIZE_ERR": WebInspector.IssueMessage.Type.RangeIssue,
+
+ "ReferenceError": WebInspector.IssueMessage.Type.ReferenceIssue,
+ "HIERARCHY_REQUEST_ERR": WebInspector.IssueMessage.Type.ReferenceIssue,
+ "INVALID_STATE_ERR": WebInspector.IssueMessage.Type.ReferenceIssue,
+ "NOT_FOUND_ERR": WebInspector.IssueMessage.Type.ReferenceIssue,
+ "WRONG_DOCUMENT_ERR": WebInspector.IssueMessage.Type.ReferenceIssue,
+
+ "TypeError": WebInspector.IssueMessage.Type.TypeIssue,
+ "INVALID_NODE_TYPE_ERR": WebInspector.IssueMessage.Type.TypeIssue,
+ "TYPE_MISMATCH_ERR": WebInspector.IssueMessage.Type.TypeIssue,
+
+ "SECURITY_ERR": WebInspector.IssueMessage.Type.SecurityIssue,
+
+ "NETWORK_ERR": WebInspector.IssueMessage.Type.NetworkIssue,
+
+ "ABORT_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "DATA_CLONE_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "INUSE_ATTRIBUTE_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "INVALID_ACCESS_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "INVALID_MODIFICATION_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "NAMESPACE_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "NOT_SUPPORTED_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "NO_DATA_ALLOWED_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "NO_MODIFICATION_ALLOWED_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "QUOTA_EXCEEDED_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "TIMEOUT_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "URL_MISMATCH_ERR": WebInspector.IssueMessage.Type.OtherIssue,
+ "VALIDATION_ERR": WebInspector.IssueMessage.Type.OtherIssue
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/KeyboardShortcut.js b/Source/WebInspectorUI/UserInterface/Models/KeyboardShortcut.js
new file mode 100644
index 000000000..43e8a7423
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/KeyboardShortcut.js
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.KeyboardShortcut = class KeyboardShortcut extends WebInspector.Object
+{
+ constructor(modifiers, key, callback, targetElement)
+ {
+ super();
+
+ console.assert(key);
+ console.assert(!callback || typeof callback === "function");
+ console.assert(!targetElement || targetElement instanceof Element);
+
+ if (typeof key === "string") {
+ key = key[0].toUpperCase();
+ key = new WebInspector.Key(key.charCodeAt(0), key);
+ }
+
+ if (callback && !targetElement)
+ targetElement = document;
+
+ this._modifiers = modifiers || WebInspector.KeyboardShortcut.Modifier.None;
+ this._key = key;
+ this._targetElement = targetElement;
+ this._callback = callback;
+ this._disabled = false;
+ this._implicitlyPreventsDefault = true;
+
+ if (targetElement) {
+ var targetKeyboardShortcuts = targetElement._keyboardShortcuts;
+ if (!targetKeyboardShortcuts)
+ targetKeyboardShortcuts = targetElement._keyboardShortcuts = [];
+
+ targetKeyboardShortcuts.push(this);
+
+ if (!WebInspector.KeyboardShortcut._registeredKeyDownListener) {
+ WebInspector.KeyboardShortcut._registeredKeyDownListener = true;
+ window.addEventListener("keydown", WebInspector.KeyboardShortcut._handleKeyDown);
+ }
+ }
+ }
+
+ // Static
+
+ static _handleKeyDown(event)
+ {
+ if (event.defaultPrevented)
+ return;
+
+ for (var targetElement = event.target; targetElement; targetElement = targetElement.parentNode) {
+ if (!targetElement._keyboardShortcuts)
+ continue;
+
+ for (var i = 0; i < targetElement._keyboardShortcuts.length; ++i) {
+ var keyboardShortcut = targetElement._keyboardShortcuts[i];
+ if (!keyboardShortcut.matchesEvent(event))
+ continue;
+
+ if (!keyboardShortcut.callback)
+ continue;
+
+ keyboardShortcut.callback(event, keyboardShortcut);
+
+ if (keyboardShortcut.implicitlyPreventsDefault)
+ event.preventDefault();
+
+ return;
+ }
+ }
+ }
+
+ // Public
+
+ get modifiers()
+ {
+ return this._modifiers;
+ }
+
+ get key()
+ {
+ return this._key;
+ }
+
+ get displayName()
+ {
+ var result = "";
+
+ if (this._modifiers & WebInspector.KeyboardShortcut.Modifier.Control)
+ result += "\u2303";
+ if (this._modifiers & WebInspector.KeyboardShortcut.Modifier.Option)
+ result += WebInspector.Platform.name === "mac" ? "\u2325" : "\u2387";
+ if (this._modifiers & WebInspector.KeyboardShortcut.Modifier.Shift)
+ result += "\u21e7";
+ if (this._modifiers & WebInspector.KeyboardShortcut.Modifier.Command)
+ result += "\u2318";
+
+ result += this._key.toString();
+
+ return result;
+ }
+
+ get callback()
+ {
+ return this._callback;
+ }
+
+ set callback(callback)
+ {
+ console.assert(!callback || typeof callback === "function");
+
+ this._callback = callback || null;
+ }
+
+ get disabled()
+ {
+ return this._disabled;
+ }
+
+ set disabled(disabled)
+ {
+ this._disabled = disabled || false;
+ }
+
+ get implicitlyPreventsDefault()
+ {
+ return this._implicitlyPreventsDefault;
+ }
+
+ set implicitlyPreventsDefault(implicitly)
+ {
+ this._implicitlyPreventsDefault = implicitly;
+ }
+
+ unbind()
+ {
+ this._disabled = true;
+
+ if (!this._targetElement)
+ return;
+
+ var targetKeyboardShortcuts = this._targetElement._keyboardShortcuts;
+ if (!targetKeyboardShortcuts)
+ return;
+
+ targetKeyboardShortcuts.remove(this);
+ }
+
+ matchesEvent(event)
+ {
+ if (this._disabled)
+ return false;
+
+ if (this._key.keyCode !== event.keyCode)
+ return false;
+
+ var eventModifiers = WebInspector.KeyboardShortcut.Modifier.None;
+ if (event.shiftKey)
+ eventModifiers |= WebInspector.KeyboardShortcut.Modifier.Shift;
+ if (event.ctrlKey)
+ eventModifiers |= WebInspector.KeyboardShortcut.Modifier.Control;
+ if (event.altKey)
+ eventModifiers |= WebInspector.KeyboardShortcut.Modifier.Option;
+ if (event.metaKey)
+ eventModifiers |= WebInspector.KeyboardShortcut.Modifier.Command;
+ return this._modifiers === eventModifiers;
+ }
+};
+
+WebInspector.Key = class Key
+{
+ constructor(keyCode, displayName)
+ {
+ this._keyCode = keyCode;
+ this._displayName = displayName;
+ }
+
+ // Public
+
+ get keyCode()
+ {
+ return this._keyCode;
+ }
+
+ get displayName()
+ {
+ return this._displayName;
+ }
+
+ toString()
+ {
+ return this._displayName;
+ }
+};
+
+WebInspector.KeyboardShortcut.Modifier = {
+ None: 0,
+ Shift: 1,
+ Control: 2,
+ Option: 4,
+ Command: 8,
+
+ get CommandOrControl()
+ {
+ return WebInspector.Platform.name === "mac" ? this.Command : this.Control;
+ }
+};
+
+WebInspector.KeyboardShortcut.Key = {
+ Backspace: new WebInspector.Key(8, "\u232b"),
+ Tab: new WebInspector.Key(9, "\u21e5"),
+ Enter: new WebInspector.Key(13, "\u21a9"),
+ Escape: new WebInspector.Key(27, "\u238b"),
+ Space: new WebInspector.Key(32, "Space"),
+ PageUp: new WebInspector.Key(33, "\u21de"),
+ PageDown: new WebInspector.Key(34, "\u21df"),
+ End: new WebInspector.Key(35, "\u2198"),
+ Home: new WebInspector.Key(36, "\u2196"),
+ Left: new WebInspector.Key(37, "\u2190"),
+ Up: new WebInspector.Key(38, "\u2191"),
+ Right: new WebInspector.Key(39, "\u2192"),
+ Down: new WebInspector.Key(40, "\u2193"),
+ Delete: new WebInspector.Key(46, "\u2326"),
+ Zero: new WebInspector.Key(48, "0"),
+ F1: new WebInspector.Key(112, "F1"),
+ F2: new WebInspector.Key(113, "F2"),
+ F3: new WebInspector.Key(114, "F3"),
+ F4: new WebInspector.Key(115, "F4"),
+ F5: new WebInspector.Key(116, "F5"),
+ F6: new WebInspector.Key(117, "F6"),
+ F7: new WebInspector.Key(118, "F7"),
+ F8: new WebInspector.Key(119, "F8"),
+ F9: new WebInspector.Key(120, "F9"),
+ F10: new WebInspector.Key(121, "F10"),
+ F11: new WebInspector.Key(122, "F11"),
+ F12: new WebInspector.Key(123, "F12"),
+ Semicolon: new WebInspector.Key(186, ";"),
+ Plus: new WebInspector.Key(187, "+"),
+ Comma: new WebInspector.Key(188, ","),
+ Minus: new WebInspector.Key(189, "-"),
+ Period: new WebInspector.Key(190, "."),
+ Slash: new WebInspector.Key(191, "/"),
+ Apostrophe: new WebInspector.Key(192, "`"),
+ LeftCurlyBrace: new WebInspector.Key(219, "{"),
+ Backslash: new WebInspector.Key(220, "\\"),
+ RightCurlyBrace: new WebInspector.Key(221, "}"),
+ SingleQuote: new WebInspector.Key(222, "\'")
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/LayoutInstrument.js b/Source/WebInspectorUI/UserInterface/Models/LayoutInstrument.js
new file mode 100644
index 000000000..2d33ac3d4
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/LayoutInstrument.js
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.LayoutInstrument = class LayoutInstrument extends WebInspector.Instrument
+{
+ // Protected
+
+ get timelineRecordType()
+ {
+ return WebInspector.TimelineRecord.Type.Layout;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/LayoutTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/LayoutTimelineRecord.js
new file mode 100644
index 000000000..79376c548
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/LayoutTimelineRecord.js
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.LayoutTimelineRecord = class LayoutTimelineRecord extends WebInspector.TimelineRecord
+{
+ constructor(eventType, startTime, endTime, callFrames, sourceCodeLocation, quad)
+ {
+ super(WebInspector.TimelineRecord.Type.Layout, startTime, endTime, callFrames, sourceCodeLocation);
+
+ console.assert(eventType);
+ console.assert(!quad || quad instanceof WebInspector.Quad);
+
+ if (eventType in WebInspector.LayoutTimelineRecord.EventType)
+ eventType = WebInspector.LayoutTimelineRecord.EventType[eventType];
+
+ this._eventType = eventType;
+ this._quad = quad || null;
+ }
+
+ // Static
+
+ static displayNameForEventType(eventType)
+ {
+ switch (eventType) {
+ case WebInspector.LayoutTimelineRecord.EventType.InvalidateStyles:
+ return WebInspector.UIString("Styles Invalidated");
+ case WebInspector.LayoutTimelineRecord.EventType.RecalculateStyles:
+ return WebInspector.UIString("Styles Recalculated");
+ case WebInspector.LayoutTimelineRecord.EventType.InvalidateLayout:
+ return WebInspector.UIString("Layout Invalidated");
+ case WebInspector.LayoutTimelineRecord.EventType.ForcedLayout:
+ return WebInspector.UIString("Forced Layout");
+ case WebInspector.LayoutTimelineRecord.EventType.Layout:
+ return WebInspector.UIString("Layout");
+ case WebInspector.LayoutTimelineRecord.EventType.Paint:
+ return WebInspector.UIString("Paint");
+ case WebInspector.LayoutTimelineRecord.EventType.Composite:
+ return WebInspector.UIString("Composite");
+ }
+ }
+
+ // Public
+
+ get eventType()
+ {
+ return this._eventType;
+ }
+
+ get width()
+ {
+ return this._quad ? this._quad.width : NaN;
+ }
+
+ get height()
+ {
+ return this._quad ? this._quad.height : NaN;
+ }
+
+ get area()
+ {
+ return this.width * this.height;
+ }
+
+ get quad()
+ {
+ return this._quad;
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ super.saveIdentityToCookie(cookie);
+
+ cookie[WebInspector.LayoutTimelineRecord.EventTypeCookieKey] = this._eventType;
+ }
+};
+
+WebInspector.LayoutTimelineRecord.EventType = {
+ InvalidateStyles: "layout-timeline-record-invalidate-styles",
+ RecalculateStyles: "layout-timeline-record-recalculate-styles",
+ InvalidateLayout: "layout-timeline-record-invalidate-layout",
+ ForcedLayout: "layout-timeline-record-forced-layout",
+ Layout: "layout-timeline-record-layout",
+ Paint: "layout-timeline-record-paint",
+ Composite: "layout-timeline-record-composite"
+};
+
+WebInspector.LayoutTimelineRecord.TypeIdentifier = "layout-timeline-record";
+WebInspector.LayoutTimelineRecord.EventTypeCookieKey = "layout-timeline-record-event-type";
diff --git a/Source/WebInspectorUI/UserInterface/Models/LazySourceCodeLocation.js b/Source/WebInspectorUI/UserInterface/Models/LazySourceCodeLocation.js
new file mode 100644
index 000000000..6f1a90ad7
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/LazySourceCodeLocation.js
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// FIXME: Investigate folding this into SourceCodeLocation proper so it can always be as lazy as possible.
+
+// Lazily compute the full SourceCodeLocation information only when such information is needed.
+// - raw information doesn't require initialization, we have that information
+// - formatted information does require initialization, done by overriding public APIs.
+// - display information does require initialization, done by overriding private funnel API resolveMappedLocation.
+
+WebInspector.LazySourceCodeLocation = class LazySourceCodeLocation extends WebInspector.SourceCodeLocation
+{
+ constructor(sourceCode, lineNumber, columnNumber)
+ {
+ super(null, lineNumber, columnNumber);
+
+ console.assert(sourceCode);
+
+ this._initialized = false;
+ this._lazySourceCode = sourceCode;
+ }
+
+ // Public
+
+ isEqual(other)
+ {
+ if (!other)
+ return false;
+ return this._lazySourceCode === other._sourceCode && this._lineNumber === other._lineNumber && this._columnNumber === other._columnNumber;
+ }
+
+ get sourceCode()
+ {
+ return this._lazySourceCode;
+ }
+
+ set sourceCode(sourceCode)
+ {
+ // Getter and setter must be provided together.
+ this.setSourceCode(sourceCode);
+ }
+
+ get formattedLineNumber()
+ {
+ this._lazyInitialization();
+ return this._formattedLineNumber;
+ }
+
+ get formattedColumnNumber()
+ {
+ this._lazyInitialization();
+ return this._formattedColumnNumber;
+ }
+
+ formattedPosition()
+ {
+ this._lazyInitialization();
+ return new WebInspector.SourceCodePosition(this._formattedLineNumber, this._formattedColumnNumber);
+ }
+
+ hasFormattedLocation()
+ {
+ this._lazyInitialization();
+ return super.hasFormattedLocation();
+ }
+
+ hasDifferentDisplayLocation()
+ {
+ this._lazyInitialization();
+ return super.hasDifferentDisplayLocation();
+ }
+
+ // Protected
+
+ resolveMappedLocation()
+ {
+ this._lazyInitialization();
+ super.resolveMappedLocation();
+ }
+
+ // Private
+
+ _lazyInitialization()
+ {
+ if (!this._initialized) {
+ this._initialized = true;
+ this.sourceCode = this._lazySourceCode;
+ }
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/LineWidget.js b/Source/WebInspectorUI/UserInterface/Models/LineWidget.js
new file mode 100644
index 000000000..06ffa1b64
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/LineWidget.js
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.LineWidget = class LineWidget extends WebInspector.Object
+{
+ constructor(codeMirrorLineWidget, widgetElement)
+ {
+ super();
+
+ console.assert(widgetElement instanceof Element);
+
+ this._codeMirrorLineWidget = codeMirrorLineWidget;
+ this._widgetElement = widgetElement;
+ }
+
+ // Public
+
+ get codeMirrorLineWidget()
+ {
+ return this._codeMirrorLineWidget;
+ }
+
+ get widgetElement()
+ {
+ return this._widgetElement;
+ }
+
+ clear()
+ {
+ this._codeMirrorLineWidget.clear();
+ }
+
+ update()
+ {
+ // FIXME: Later version of CodeMirror has update.
+ if (this._codeMirrorLineWidget.update)
+ this._codeMirrorLineWidget.update();
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/LogObject.js b/Source/WebInspectorUI/UserInterface/Models/LogObject.js
new file mode 100644
index 000000000..b9551226b
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/LogObject.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.LogObject = class LogObject extends WebInspector.Object
+{
+ constructor()
+ {
+ super();
+
+ this._startDate = new Date;
+ }
+
+ // Public
+
+ get startDate()
+ {
+ return this._startDate;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/MemoryCategory.js b/Source/WebInspectorUI/UserInterface/Models/MemoryCategory.js
new file mode 100644
index 000000000..76cbba717
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/MemoryCategory.js
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.MemoryCategory = class MemoryCategory extends WebInspector.Object
+{
+ constructor(type, size)
+ {
+ super();
+
+ console.assert(typeof type === "string");
+ console.assert(typeof size === "number");
+ console.assert(size >= 0);
+
+ this.type = type;
+ this.size = size;
+ }
+};
+
+WebInspector.MemoryCategory.Type = {
+ JavaScript: "javascript",
+ Images: "images",
+ Layers: "layers",
+ Page: "page",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/MemoryInstrument.js b/Source/WebInspectorUI/UserInterface/Models/MemoryInstrument.js
new file mode 100644
index 000000000..cbe254394
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/MemoryInstrument.js
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.MemoryInstrument = class MemoryInstrument extends WebInspector.Instrument
+{
+ constructor()
+ {
+ super();
+
+ console.assert(WebInspector.MemoryInstrument.supported());
+ }
+
+ // Static
+
+ static supported()
+ {
+ // COMPATIBILITY (iOS 9): MemoryAgent did not exist.
+ return window.MemoryAgent;
+ }
+
+ // Protected
+
+ get timelineRecordType()
+ {
+ return WebInspector.TimelineRecord.Type.Memory;
+ }
+
+ startInstrumentation(initiatedByBackend)
+ {
+ if (!initiatedByBackend)
+ MemoryAgent.startTracking();
+ }
+
+ stopInstrumentation(initiatedByBackend)
+ {
+ if (!initiatedByBackend)
+ MemoryAgent.stopTracking();
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/MemoryPressureEvent.js b/Source/WebInspectorUI/UserInterface/Models/MemoryPressureEvent.js
new file mode 100644
index 000000000..33aeb8dac
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/MemoryPressureEvent.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.MemoryPressureEvent = class MemoryPressureEvent extends WebInspector.Object
+{
+ constructor(timestamp, severity)
+ {
+ super();
+
+ this._timestamp = timestamp;
+ this._severity = severity;
+ }
+
+ // Static
+
+ static fromPayload(timestamp, protocolSeverity)
+ {
+ let severity;
+ switch (protocolSeverity) {
+ case MemoryAgent.MemoryPressureSeverity.Critical:
+ severity = WebInspector.MemoryPressureEvent.Severity.Critical;
+ break;
+ case MemoryAgent.MemoryPressureSeverity.NonCritical:
+ severity = WebInspector.MemoryPressureEvent.Severity.NonCritical;
+ break;
+ default:
+ console.error("Unexpected memory pressure severity", protocolSeverity);
+ severity = WebInspector.MemoryPressureEvent.Severity.NonCritical;
+ break;
+ }
+
+ return new WebInspector.MemoryPressureEvent(timestamp, severity);
+ }
+
+ // Public
+
+ get timestamp() { return this._timestamp; }
+ get severity() { return this._severity; }
+};
+
+WebInspector.MemoryPressureEvent.Severity = {
+ Critical: Symbol("Critical"),
+ NonCritical: Symbol("NonCritical"),
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/MemoryTimeline.js b/Source/WebInspectorUI/UserInterface/Models/MemoryTimeline.js
new file mode 100644
index 000000000..8a791bab7
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/MemoryTimeline.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.MemoryTimeline = class MemoryTimeline extends WebInspector.Timeline
+{
+ // Public
+
+ get memoryPressureEvents() { return this._pressureEvents; }
+
+ addMemoryPressureEvent(memoryPressureEvent)
+ {
+ console.assert(memoryPressureEvent instanceof WebInspector.MemoryPressureEvent);
+
+ this._pressureEvents.push(memoryPressureEvent);
+
+ this.dispatchEventToListeners(WebInspector.MemoryTimeline.Event.MemoryPressureEventAdded, {memoryPressureEvent});
+ }
+
+ // Protected
+
+ reset(suppressEvents)
+ {
+ super.reset(suppressEvents);
+
+ this._pressureEvents = [];
+ }
+};
+
+WebInspector.MemoryTimeline.Event = {
+ MemoryPressureEventAdded: "memory-timeline-memory-pressure-event-added",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js
new file mode 100644
index 000000000..5b482d823
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.MemoryTimelineRecord = class MemoryTimelineRecord extends WebInspector.TimelineRecord
+{
+ constructor(timestamp, categories)
+ {
+ super(WebInspector.TimelineRecord.Type.Memory, timestamp, timestamp);
+
+ console.assert(typeof timestamp === "number");
+ console.assert(categories instanceof Array);
+
+ this._timestamp = timestamp;
+ this._categories = WebInspector.MemoryTimelineRecord.memoryCategoriesFromProtocol(categories);
+
+ this._totalSize = 0;
+ for (let {size} of categories)
+ this._totalSize += size;
+ }
+
+ // Static
+
+ static memoryCategoriesFromProtocol(categories)
+ {
+ let javascriptSize = 0;
+ let imagesSize = 0;
+ let layersSize = 0;
+ let pageSize = 0;
+
+ for (let {type, size} of categories) {
+ switch (type) {
+ case MemoryAgent.CategoryDataType.Javascript:
+ case MemoryAgent.CategoryDataType.JIT:
+ javascriptSize += size;
+ break;
+ case MemoryAgent.CategoryDataType.Images:
+ imagesSize += size;
+ break;
+ case MemoryAgent.CategoryDataType.Layers:
+ layersSize += size;
+ break;
+ case MemoryAgent.CategoryDataType.Page:
+ case MemoryAgent.CategoryDataType.Other:
+ pageSize += size;
+ break;
+ default:
+ console.warn("Unhandled Memory.CategoryDataType: " + type);
+ break;
+ }
+ }
+
+ return [
+ {type: WebInspector.MemoryCategory.Type.JavaScript, size: javascriptSize},
+ {type: WebInspector.MemoryCategory.Type.Images, size: imagesSize},
+ {type: WebInspector.MemoryCategory.Type.Layers, size: layersSize},
+ {type: WebInspector.MemoryCategory.Type.Page, size: pageSize},
+ ];
+ }
+
+ // Public
+
+ get timestamp() { return this._timestamp; }
+ get categories() { return this._categories; }
+ get totalSize() { return this._totalSize; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js b/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js
new file mode 100644
index 000000000..0633a209e
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js
@@ -0,0 +1,2232 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// FIXME: Provide Parameter lists for the constructors themselves? (new RegExp(...)).
+// FIXME: Provide Parameter lists for global functions (eval, decodeURI, ...).
+
+WebInspector.NativeConstructorFunctionParameters = {
+ Object: {
+ assign: "target, ...sources",
+ create: "prototype, [propertiesObject]",
+ defineProperties: "object, properties",
+ defineProperty: "object, propertyName, descriptor",
+ freeze: "object",
+ getOwnPropertyDescriptor: "object, propertyName",
+ getOwnPropertyNames: "object",
+ getOwnPropertySymbols: "object",
+ getPrototypeOf: "object",
+ is: "value1, value2",
+ isExtensible: "object",
+ isFrozen: "object",
+ isSealed: "object",
+ keys: "object",
+ preventExtensions: "object",
+ seal: "object",
+ setPrototypeOf: "object, prototype",
+ __proto__: null,
+ },
+
+ Array: {
+ from: "arrayLike, [mapFunction], [thisArg]",
+ isArray: "object",
+ of: "[...values]",
+ __proto__: null,
+ },
+
+ ArrayBuffer: {
+ isView: "object",
+ transfer: "oldBuffer, [newByteLength=length]",
+ __proto__: null,
+ },
+
+ Number: {
+ isFinite: "value",
+ isInteger: "value",
+ isNaN: "value",
+ isSafeInteger: "value",
+ parseFloat: "string",
+ parseInt: "string, [radix]",
+ __proto__: null,
+ },
+
+ Math: {
+ abs: "x",
+ acos: "x",
+ acosh: "x",
+ asin: "x",
+ asinh: "x",
+ atan2: "y, x",
+ atan: "x",
+ atanh: "x",
+ cbrt: "x",
+ ceil: "x",
+ clz32: "x",
+ cos: "x",
+ cosh: "x",
+ exp: "x",
+ expm1: "x",
+ floor: "x",
+ fround: "x",
+ hypot: "[...x]",
+ imul: "x",
+ log: "x",
+ log1p: "x",
+ log2: "x",
+ log10: "x",
+ max: "[...x]",
+ min: "[...x]",
+ pow: "x, y",
+ round: "x",
+ sign: "x",
+ sin: "x",
+ sinh: "x",
+ sqrt: "x",
+ tan: "x",
+ tanh: "x",
+ trunc: "x",
+ __proto__: null,
+ },
+
+ JSON: {
+ parse: "text, [reviver]",
+ stringify: "value, [replacer], [space]",
+ __proto__: null,
+ },
+
+ Date: {
+ parse: "dateString",
+ UTC: "year, [month], [day], [hour], [minute], [second], [ms]",
+ __proto__: null,
+ },
+
+ Promise: {
+ all: "iterable",
+ race: "iterable",
+ reject: "reason",
+ resolve: "value",
+ __proto__: null,
+ },
+
+ Reflect: {
+ apply: "target, thisArgument, argumentsList",
+ construct: "target, argumentsList, [newTarget=target]",
+ defineProperty: "target, propertyKey, attributes",
+ deleteProperty: "target, propertyKey",
+ enumerate: "target",
+ get: "target, propertyKey, [receiver]",
+ getOwnPropertyDescriptor: "target, propertyKey",
+ getPrototypeOf: "target",
+ has: "target, propertyKey",
+ isExtensible: "target",
+ ownKeys: "target",
+ preventExtensions: "target",
+ set: "target, propertyKey, value, [receiver]",
+ setPrototypeOf: "target, prototype",
+ __proto__: null,
+ },
+
+ String: {
+ fromCharCode: "...codeUnits",
+ fromCodePoint: "...codePoints",
+ raw: "template, ...substitutions",
+ __proto__: null,
+ },
+
+ Symbol: {
+ for: "key",
+ keyFor: "symbol",
+ __proto__: null,
+ },
+
+ Console: {
+ assert: "condition, [message], [...values]",
+ count: "[label]",
+ debug: "message, [...values]",
+ dir: "object",
+ dirxml: "object",
+ error: "message, [...values]",
+ group: "[name]",
+ groupCollapsed: "[name]",
+ groupEnd: "[name]",
+ info: "message, [...values]",
+ log: "message, [...values]",
+ profile: "name",
+ profileEnd: "name",
+ table: "data, [columns]",
+ takeHeapSnapshot: "[label]",
+ time: "name = \"default\"",
+ timeEnd: "name = \"default\"",
+ timeStamp: "[label]",
+ trace: "message, [...values]",
+ warn: "message, [...values]",
+ __proto__: null,
+ },
+
+ // Autogenerated DOM Interface static methods.
+
+ IDBKeyRangeConstructor: {
+ bound: "lower, upper, [lowerOpen], [upperOpen]",
+ lowerBound: "lower, [open]",
+ only: "value",
+ upperBound: "upper, [open]",
+ __proto__: null,
+ },
+
+ MediaSourceConstructor: {
+ isTypeSupported: "type",
+ __proto__: null,
+ },
+
+ MediaStreamTrackConstructor: {
+ getSources: "callback",
+ __proto__: null,
+ },
+
+ NotificationConstructor: {
+ requestPermission: "[callback]",
+ __proto__: null,
+ },
+
+ URLConstructor: {
+ createObjectURL: "blob",
+ revokeObjectURL: "url",
+ __proto__: null,
+ },
+
+ WebKitMediaKeysConstructor: {
+ isTypeSupported: "keySystem, [type]",
+ __proto__: null,
+ },
+};
+
+WebInspector.NativePrototypeFunctionParameters = {
+
+ // Built-in JavaScript objects.
+ // FIXME: TypedArrays (Int8Array, etc),
+
+ Object: {
+ __defineGetter__: "propertyName, getterFunction",
+ __defineSetter__: "propertyName, setterFunction",
+ __lookupGetter__: "propertyName",
+ __lookupSetter__: "propertyName",
+ hasOwnProperty: "propertyName",
+ isPrototypeOf: "property",
+ propertyIsEnumerable: "propertyName",
+ __proto__: null,
+ },
+
+ Array: {
+ concat: "value, ...",
+ copyWithin: "targetIndex, startIndex, [endIndex=length]",
+ every: "callback, [thisArg]",
+ fill: "value, [startIndex=0], [endIndex=length]",
+ filter: "callback, [thisArg]",
+ find: "callback, [thisArg]",
+ findIndex: "callback, [thisArg]",
+ forEach: "callback, [thisArg]",
+ includes: "searchValue, [startIndex=0]",
+ indexOf: "searchValue, [startIndex=0]",
+ join: "[separator=\",\"]",
+ lastIndexOf: "searchValue, [startIndex=length]",
+ map: "callback, [thisArg]",
+ push: "value, ...",
+ reduce: "callback, [initialValue]",
+ reduceRight: "callback, [initialValue]",
+ slice: "[startIndex=0], [endIndex=length]",
+ some: "callback, [thisArg]",
+ sort: "[compareFunction]",
+ splice: "startIndex, [deleteCount=0], ...itemsToAdd",
+ __proto__: null,
+ },
+
+ ArrayBuffer: {
+ slice: "startIndex, [endIndex=byteLength]",
+ __proto__: null,
+ },
+
+ DataView: {
+ setInt8: "byteOffset, value",
+ setInt16: "byteOffset, value, [littleEndian=false]",
+ setInt23: "byteOffset, value, [littleEndian=false]",
+ setUint8: "byteOffset, value",
+ setUint16: "byteOffset, value, [littleEndian=false]",
+ setUint32: "byteOffset, value, [littleEndian=false]",
+ setFloat32: "byteOffset, value, [littleEndian=false]",
+ setFloat64: "byteOffset, value, [littleEndian=false]",
+ getInt8: "byteOffset",
+ getInt16: "byteOffset, [littleEndian=false]",
+ getInt23: "byteOffset, [littleEndian=false]",
+ getUint8: "byteOffset",
+ getUint16: "byteOffset, [littleEndian=false]",
+ getUint32: "byteOffset, [littleEndian=false]",
+ getFloat32: "byteOffset, [littleEndian=false]",
+ getFloat64: "byteOffset, [littleEndian=false]",
+ __proto__: null,
+ },
+
+ Date: {
+ setDate: "day",
+ setFullYear: "year, [month=getMonth()], [day=getDate()]",
+ setHours: "hours, [minutes=getMinutes()], [seconds=getSeconds()], [ms=getMilliseconds()]",
+ setMilliseconds: "ms",
+ setMinutes: "minutes, [seconds=getSeconds()], [ms=getMilliseconds()]",
+ setMonth: "month, [day=getDate()]",
+ setSeconds: "seconds, [ms=getMilliseconds()]",
+ setTime: "time",
+ setUTCDate: "day",
+ setUTCFullYear: "year, [month=getUTCMonth()], [day=getUTCDate()]",
+ setUTCHours: "hours, [minutes=getUTCMinutes()], [seconds=getUTCSeconds()], [ms=getUTCMilliseconds()]",
+ setUTCMilliseconds: "ms",
+ setUTCMinutes: "minutes, [seconds=getUTCSeconds()], [ms=getUTCMilliseconds()]",
+ setUTCMonth: "month, [day=getUTCDate()]",
+ setUTCSeconds: "seconds, [ms=getUTCMilliseconds()]",
+ setUTCTime: "time",
+ setYear: "year",
+ __proto__: null,
+ },
+
+ Function: {
+ apply: "thisObject, [argumentsArray]",
+ bind: "thisObject, ...arguments",
+ call: "thisObject, ...arguments",
+ __proto__: null,
+ },
+
+ Map: {
+ delete: "key",
+ forEach: "callback, [thisArg]",
+ get: "key",
+ has: "key",
+ set: "key, value",
+ __proto__: null,
+ },
+
+ Number: {
+ toExponential: "[digits]",
+ toFixed: "[digits]",
+ toPrecision: "[significantDigits]",
+ toString: "[radix=10]",
+ __proto__: null,
+ },
+
+ RegExp: {
+ compile: "pattern, flags",
+ exec: "string",
+ test: "string",
+ __proto__: null,
+ },
+
+ Set: {
+ delete: "value",
+ forEach: "callback, [thisArg]",
+ has: "value",
+ add: "value",
+ __proto__: null,
+ },
+
+ String: {
+ charAt: "index",
+ charCodeAt: "index",
+ codePoints: "index",
+ concat: "string, ...",
+ includes: "searchValue, [startIndex=0]",
+ indexOf: "searchValue, [startIndex=0]",
+ lastIndexOf: "searchValue, [startIndex=length]",
+ localeCompare: "string",
+ match: "regex",
+ repeat: "count",
+ replace: "regex|string, replaceString|replaceHandler, [flags]",
+ search: "regex",
+ slice: "startIndex, [endIndex=length]",
+ split: "[separator], [limit]",
+ substr: "startIndex, [numberOfCharacters]",
+ substring: "startIndex, [endIndex=length]",
+ __proto__: null,
+ },
+
+ WeakMap: {
+ delete: "key",
+ get: "key",
+ has: "key",
+ set: "key, value",
+ __proto__: null,
+ },
+
+ WeakSet: {
+ delete: "value",
+ has: "value",
+ add: "value",
+ __proto__: null,
+ },
+
+ Promise: {
+ catch: "rejectionHandler",
+ then: "resolvedHandler, rejectionHandler",
+ __proto__: null,
+ },
+
+ Generator: {
+ next: "value",
+ return: "value",
+ throw: "exception",
+ __proto__: null,
+ },
+
+ // Curated DOM Interfaces.
+
+ Element: {
+ closest: "selectors",
+ getAttribute: "attributeName",
+ getAttributeNS: "namespace, attributeName",
+ getAttributeNode: "attributeName",
+ getAttributeNodeNS: "namespace, attributeName",
+ hasAttribute: "attributeName",
+ hasAttributeNS: "namespace, attributeName",
+ matches: "selector",
+ removeAttribute: "attributeName",
+ removeAttributeNS: "namespace, attributeName",
+ removeAttributeNode: "attributeName",
+ scrollByLines: "[lines]",
+ scrollByPages: "[pages]",
+ scrollIntoView: "[alignWithTop]",
+ scrollIntoViewIfNeeded: "[centerIfNeeded]",
+ setAttribute: "name, value",
+ setAttributeNS: "namespace, name, value",
+ setAttributeNode: "attributeNode",
+ setAttributeNodeNS: "namespace, attributeNode",
+ webkitMatchesSelector: "selectors",
+ __proto__: null,
+ },
+
+ Node: {
+ appendChild: "child",
+ cloneNode: "[deep]",
+ compareDocumentPosition: "[node]",
+ contains: "[node]",
+ insertBefore: "insertElement, referenceElement",
+ isDefaultNamespace: "[namespace]",
+ isEqualNode: "[node]",
+ lookupNamespaceURI: "prefix",
+ removeChild: "node",
+ replaceChild: "newChild, oldChild",
+ __proto__: null,
+ },
+
+ Window: {
+ alert: "[message]",
+ atob: "encodedData",
+ btoa: "stringToEncode",
+ cancelAnimationFrame: "id",
+ clearInterval: "intervalId",
+ clearTimeout: "timeoutId",
+ confirm: "[message]",
+ find: "string, [caseSensitive], [backwards], [wrapAround]",
+ getComputedStyle: "[element], [pseudoElement]",
+ getMatchedCSSRules: "[element], [pseudoElement]",
+ matchMedia: "mediaQueryString",
+ moveBy: "[deltaX], [deltaY]",
+ moveTo: "[screenX], [screenY]",
+ open: "url, windowName, [featuresString]",
+ openDatabase: "name, version, displayName, estimatedSize, [creationCallback]",
+ postMessage: "message, targetOrigin, [...transferables]",
+ prompt: "[message], [value]",
+ requestAnimationFrame: "callback",
+ resizeBy: "[deltaX], [deltaY]",
+ resizeTo: "[width], [height]",
+ scrollBy: "[deltaX], [deltaY]",
+ scrollTo: "[x], [y]",
+ setInterval: "func, [delay], [...params]",
+ setTimeout: "func, [delay], [...params]",
+ showModalDialog: "url, [arguments], [options]",
+ __proto__: null,
+ },
+
+ Document: {
+ adoptNode: "[node]",
+ caretRangeFromPoint: "[x], [y]",
+ createAttribute: "attributeName",
+ createAttributeNS: "namespace, qualifiedName",
+ createCDATASection: "data",
+ createComment: "data",
+ createElement: "tagName",
+ createElementNS: "namespace, qualifiedName",
+ createEntityReference: "name",
+ createEvent: "type",
+ createExpression: "xpath, resolver",
+ createNSResolver: "node",
+ createNodeIterator: "root, whatToShow, filter",
+ createProcessingInstruction: "target, data",
+ createTextNode: "data",
+ createTreeWalker: "root, whatToShow, filter, entityReferenceExpansion",
+ elementFromPoint: "x, y",
+ evaluate: "xpath, contextNode, namespaceResolver, resultType, result",
+ execCommand: "command, userInterface, value",
+ getCSSCanvasContext: "contextId, name, width, height",
+ getElementById: "id",
+ getElementsByName: "name",
+ getOverrideStyle: "[element], [pseudoElement]",
+ importNode: "node, deep",
+ queryCommandEnabled: "command",
+ queryCommandIndeterm: "command",
+ queryCommandState: "command",
+ queryCommandSupported: "command",
+ queryCommandValue: "command",
+ __proto__: null,
+ },
+
+ // Autogenerated DOM Interfaces.
+
+ ANGLEInstancedArrays: {
+ drawArraysInstancedANGLE: "mode, first, count, primcount",
+ drawElementsInstancedANGLE: "mode, count, type, offset, primcount",
+ vertexAttribDivisorANGLE: "index, divisor",
+ __proto__: null,
+ },
+
+ AnalyserNode: {
+ getByteFrequencyData: "array",
+ getByteTimeDomainData: "array",
+ getFloatFrequencyData: "array",
+ __proto__: null,
+ },
+
+ AudioBuffer: {
+ getChannelData: "channelIndex",
+ __proto__: null,
+ },
+
+ AudioBufferCallback: {
+ handleEvent: "audioBuffer",
+ __proto__: null,
+ },
+
+ AudioBufferSourceNode: {
+ noteGrainOn: "when, grainOffset, grainDuration",
+ noteOff: "when",
+ noteOn: "when",
+ start: "[when], [grainOffset], [grainDuration]",
+ stop: "[when]",
+ __proto__: null,
+ },
+
+ AudioListener: {
+ setOrientation: "x, y, z, xUp, yUp, zUp",
+ setPosition: "x, y, z",
+ setVelocity: "x, y, z",
+ __proto__: null,
+ },
+
+ AudioNode: {
+ connect: "destination, [output], [input]",
+ disconnect: "[output]",
+ __proto__: null,
+ },
+
+ AudioParam: {
+ cancelScheduledValues: "startTime",
+ exponentialRampToValueAtTime: "value, time",
+ linearRampToValueAtTime: "value, time",
+ setTargetAtTime: "target, time, timeConstant",
+ setTargetValueAtTime: "targetValue, time, timeConstant",
+ setValueAtTime: "value, time",
+ setValueCurveAtTime: "values, time, duration",
+ __proto__: null,
+ },
+
+ AudioTrackList: {
+ getTrackById: "id",
+ item: "index",
+ __proto__: null,
+ },
+
+ BiquadFilterNode: {
+ getFrequencyResponse: "frequencyHz, magResponse, phaseResponse",
+ __proto__: null,
+ },
+
+ Blob: {
+ slice: "[start], [end], [contentType]",
+ __proto__: null,
+ },
+
+ CSS: {
+ supports: "property, value",
+ __proto__: null,
+ },
+
+ CSSKeyframesRule: {
+ appendRule: "[rule]",
+ deleteRule: "[key]",
+ findRule: "[key]",
+ insertRule: "[rule]",
+ __proto__: null,
+ },
+
+ CSSMediaRule: {
+ deleteRule: "[index]",
+ insertRule: "[rule], [index]",
+ __proto__: null,
+ },
+
+ CSSPrimitiveValue: {
+ getFloatValue: "[unitType]",
+ setFloatValue: "[unitType], [floatValue]",
+ setStringValue: "[stringType], [stringValue]",
+ __proto__: null,
+ },
+
+ CSSRuleList: {
+ item: "[index]",
+ __proto__: null,
+ },
+
+ CSSStyleDeclaration: {
+ getPropertyCSSValue: "[propertyName]",
+ getPropertyPriority: "[propertyName]",
+ getPropertyShorthand: "[propertyName]",
+ getPropertyValue: "[propertyName]",
+ isPropertyImplicit: "[propertyName]",
+ item: "[index]",
+ removeProperty: "[propertyName]",
+ setProperty: "[propertyName], [value], [priority]",
+ __proto__: null,
+ },
+
+ CSSStyleSheet: {
+ addRule: "[selector], [style], [index]",
+ deleteRule: "[index]",
+ insertRule: "[rule], [index]",
+ removeRule: "[index]",
+ __proto__: null,
+ },
+
+ CSSSupportsRule: {
+ deleteRule: "[index]",
+ insertRule: "[rule], [index]",
+ __proto__: null,
+ },
+
+ CSSValueList: {
+ item: "[index]",
+ __proto__: null,
+ },
+
+ CanvasGradient: {
+ addColorStop: "[offset], [color]",
+ __proto__: null,
+ },
+
+ CanvasRenderingContext2D: {
+ arc: "x, y, radius, startAngle, endAngle, [anticlockwise]",
+ arcTo: "x1, y1, x2, y2, radius",
+ bezierCurveTo: "cp1x, cp1y, cp2x, cp2y, x, y",
+ clearRect: "x, y, width, height",
+ clip: "path, [winding]",
+ createImageData: "imagedata",
+ createLinearGradient: "x0, y0, x1, y1",
+ createPattern: "canvas, repetitionType",
+ createRadialGradient: "x0, y0, r0, x1, y1, r1",
+ drawFocusIfNeeded: "element",
+ drawImage: "image, x, y",
+ drawImageFromRect: "image, [sx], [sy], [sw], [sh], [dx], [dy], [dw], [dh], [compositeOperation]",
+ ellipse: "x, y, radiusX, radiusY, rotation, startAngle, endAngle, [anticlockwise]",
+ fill: "path, [winding]",
+ fillRect: "x, y, width, height",
+ fillText: "text, x, y, [maxWidth]",
+ getImageData: "sx, sy, sw, sh",
+ isPointInPath: "path, x, y, [winding]",
+ isPointInStroke: "path, x, y",
+ lineTo: "x, y",
+ measureText: "text",
+ moveTo: "x, y",
+ putImageData: "imagedata, dx, dy",
+ quadraticCurveTo: "cpx, cpy, x, y",
+ rect: "x, y, width, height",
+ rotate: "angle",
+ scale: "sx, sy",
+ setAlpha: "[alpha]",
+ setCompositeOperation: "[compositeOperation]",
+ setFillColor: "color, [alpha]",
+ setLineCap: "[cap]",
+ setLineDash: "dash",
+ setLineJoin: "[join]",
+ setLineWidth: "[width]",
+ setMiterLimit: "[limit]",
+ setShadow: "width, height, blur, [color], [alpha]",
+ setStrokeColor: "color, [alpha]",
+ setTransform: "m11, m12, m21, m22, dx, dy",
+ stroke: "path",
+ strokeRect: "x, y, width, height",
+ strokeText: "text, x, y, [maxWidth]",
+ transform: "m11, m12, m21, m22, dx, dy",
+ translate: "tx, ty",
+ webkitGetImageDataHD: "sx, sy, sw, sh",
+ webkitPutImageDataHD: "imagedata, dx, dy",
+ __proto__: null,
+ },
+
+ CharacterData: {
+ appendData: "[data]",
+ deleteData: "[offset], [length]",
+ insertData: "[offset], [data]",
+ replaceData: "[offset], [length], [data]",
+ substringData: "[offset], [length]",
+ __proto__: null,
+ },
+
+ ClientRectList: {
+ item: "[index]",
+ __proto__: null,
+ },
+
+ CommandLineAPIHost: {
+ copyText: "text",
+ databaseId: "database",
+ getEventListeners: "node",
+ inspect: "objectId, hints",
+ storageId: "storage",
+ __proto__: null,
+ },
+
+ CompositionEvent: {
+ initCompositionEvent: "[typeArg], [canBubbleArg], [cancelableArg], [viewArg], [dataArg]",
+ __proto__: null,
+ },
+
+ Crypto: {
+ getRandomValues: "array",
+ __proto__: null,
+ },
+
+ CustomElementRegistry: {
+ define: "name, constructor",
+ get: "name",
+ whenDefined: "name",
+ __proto__: null,
+ },
+
+ CustomEvent: {
+ initCustomEvent: "[typeArg], [canBubbleArg], [cancelableArg], [detailArg]",
+ __proto__: null,
+ },
+
+ DOMApplicationCache: {
+ /* EventTarget */
+ __proto__: null,
+ },
+
+ DOMImplementation: {
+ createCSSStyleSheet: "[title], [media]",
+ createDocument: "[namespaceURI], [qualifiedName], [doctype]",
+ createDocumentType: "[qualifiedName], [publicId], [systemId]",
+ createHTMLDocument: "[title]",
+ hasFeature: "[feature], [version]",
+ __proto__: null,
+ },
+
+ DOMParser: {
+ parseFromString: "[str], [contentType]",
+ __proto__: null,
+ },
+
+ DOMStringList: {
+ contains: "[string]",
+ item: "[index]",
+ __proto__: null,
+ },
+
+ DOMTokenList: {
+ add: "tokens...",
+ contains: "token",
+ item: "index",
+ remove: "tokens...",
+ toggle: "token, [force]",
+ __proto__: null,
+ },
+
+ DataTransfer: {
+ clearData: "[type]",
+ getData: "type",
+ setData: "type, data",
+ setDragImage: "image, x, y",
+ __proto__: null,
+ },
+
+ DataTransferItem: {
+ getAsString: "[callback]",
+ __proto__: null,
+ },
+
+ DataTransferItemList: {
+ add: "file",
+ item: "[index]",
+ __proto__: null,
+ },
+
+ Database: {
+ changeVersion: "oldVersion, newVersion, [callback], [errorCallback], [successCallback]",
+ readTransaction: "callback, [errorCallback], [successCallback]",
+ transaction: "callback, [errorCallback], [successCallback]",
+ __proto__: null,
+ },
+
+ DatabaseCallback: {
+ handleEvent: "database",
+ __proto__: null,
+ },
+
+ DedicatedWorkerGlobalScope: {
+ postMessage: "message, [messagePorts]",
+ __proto__: null,
+ },
+
+ DeviceMotionEvent: {
+ initDeviceMotionEvent: "[type], [bubbles], [cancelable], [acceleration], [accelerationIncludingGravity], [rotationRate], [interval]",
+ __proto__: null,
+ },
+
+ DeviceOrientationEvent: {
+ initDeviceOrientationEvent: "[type], [bubbles], [cancelable], [alpha], [beta], [gamma], [absolute]",
+ __proto__: null,
+ },
+
+ DocumentFragment: {
+ getElementById: "id",
+ querySelector: "selectors",
+ querySelectorAll: "selectors",
+ __proto__: null,
+ },
+
+ Event: {
+ initEvent: "type, canBubble, cancelable",
+ __proto__: null,
+ },
+
+ FileList: {
+ item: "index",
+ __proto__: null,
+ },
+
+ FileReader: {
+ readAsArrayBuffer: "blob",
+ readAsBinaryString: "blob",
+ readAsDataURL: "blob",
+ readAsText: "blob, [encoding]",
+ __proto__: null,
+ },
+
+ FileReaderSync: {
+ readAsArrayBuffer: "blob",
+ readAsBinaryString: "blob",
+ readAsDataURL: "blob",
+ readAsText: "blob, [encoding]",
+ __proto__: null,
+ },
+
+ FontFaceSet: {
+ add: "font",
+ check: "font, [text=\" \"]",
+ delete: "font",
+ load: "font, [text=\" \"]",
+ __proto__: null,
+ },
+
+ FormData: {
+ append: "[name], [value], [filename]",
+ __proto__: null,
+ },
+
+ Geolocation: {
+ clearWatch: "watchID",
+ getCurrentPosition: "successCallback, [errorCallback], [options]",
+ watchPosition: "successCallback, [errorCallback], [options]",
+ __proto__: null,
+ },
+
+ HTMLAllCollection: {
+ item: "[index]",
+ namedItem: "name",
+ tags: "name",
+ __proto__: null,
+ },
+
+ HTMLButtonElement: {
+ setCustomValidity: "error",
+ __proto__: null,
+ },
+
+ HTMLCanvasElement: {
+ getContext: "contextId",
+ toDataURL: "[type]",
+ __proto__: null,
+ },
+
+ HTMLCollection: {
+ item: "[index]",
+ namedItem: "[name]",
+ __proto__: null,
+ },
+
+ HTMLDocument: {
+ write: "[html]",
+ writeln: "[html]",
+ __proto__: null,
+ },
+
+ HTMLElement: {
+ insertAdjacentElement: "[position], [element]",
+ insertAdjacentHTML: "[position], [html]",
+ insertAdjacentText: "[position], [text]",
+ __proto__: null,
+ },
+
+ HTMLFieldSetElement: {
+ setCustomValidity: "error",
+ __proto__: null,
+ },
+
+ HTMLFormControlsCollection: {
+ namedItem: "[name]",
+ __proto__: null,
+ },
+
+ HTMLInputElement: {
+ setCustomValidity: "error",
+ setRangeText: "replacement",
+ setSelectionRange: "start, end, [direction]",
+ stepDown: "[n]",
+ stepUp: "[n]",
+ __proto__: null,
+ },
+
+ HTMLKeygenElement: {
+ setCustomValidity: "error",
+ __proto__: null,
+ },
+
+ HTMLMediaElement: {
+ addTextTrack: "kind, [label], [language]",
+ canPlayType: "[type], [keySystem]",
+ fastSeek: "time",
+ webkitAddKey: "keySystem, key, [initData], [sessionId]",
+ webkitCancelKeyRequest: "keySystem, [sessionId]",
+ webkitGenerateKeyRequest: "keySystem, [initData]",
+ webkitSetMediaKeys: "mediaKeys",
+ __proto__: null,
+ },
+
+ HTMLObjectElement: {
+ setCustomValidity: "error",
+ __proto__: null,
+ },
+
+ HTMLOptionsCollection: {
+ add: "element, [before]",
+ namedItem: "[name]",
+ remove: "[index]",
+ __proto__: null,
+ },
+
+ HTMLOutputElement: {
+ setCustomValidity: "error",
+ __proto__: null,
+ },
+
+ HTMLSelectElement: {
+ add: "element, [before]",
+ item: "index",
+ namedItem: "[name]",
+ setCustomValidity: "error",
+ __proto__: null,
+ },
+
+ HTMLSlotElement: {
+ assignedNodes: "[options]",
+ __proto__: null,
+ },
+
+ HTMLTableElement: {
+ deleteRow: "index",
+ insertRow: "[index]",
+ __proto__: null,
+ },
+
+ HTMLTableRowElement: {
+ deleteCell: "index",
+ insertCell: "[index]",
+ __proto__: null,
+ },
+
+ HTMLTableSectionElement: {
+ deleteRow: "index",
+ insertRow: "[index]",
+ __proto__: null,
+ },
+
+ HTMLTextAreaElement: {
+ setCustomValidity: "error",
+ setRangeText: "replacement",
+ setSelectionRange: "[start], [end], [direction]",
+ __proto__: null,
+ },
+
+ HTMLVideoElement: {
+ webkitSetPresentationMode: "mode",
+ webkitSupportsPresentationMode: "mode",
+ __proto__: null,
+ },
+
+ HashChangeEvent: {
+ initHashChangeEvent: "[type], [canBubble], [cancelable], [oldURL], [newURL]",
+ __proto__: null,
+ },
+
+ History: {
+ go: "[distance]",
+ pushState: "data, title, [url]",
+ replaceState: "data, title, [url]",
+ __proto__: null,
+ },
+
+ IDBCursor: {
+ advance: "count",
+ continue: "[key]",
+ update: "value",
+ __proto__: null,
+ },
+
+ IDBDatabase: {
+ createObjectStore: "name, [options]",
+ deleteObjectStore: "name",
+ transaction: "storeName, [mode]",
+ __proto__: null,
+ },
+
+ IDBFactory: {
+ cmp: "first, second",
+ deleteDatabase: "name",
+ open: "name, [version]",
+ __proto__: null,
+ },
+
+ IDBIndex: {
+ count: "[range]",
+ get: "key",
+ getKey: "key",
+ openCursor: "[range], [direction]",
+ openKeyCursor: "[range], [direction]",
+ __proto__: null,
+ },
+
+ IDBObjectStore: {
+ add: "value, [key]",
+ count: "[range]",
+ createIndex: "name, keyPath, [options]",
+ delete: "keyRange",
+ deleteIndex: "name",
+ get: "key",
+ index: "name",
+ openCursor: "[range], [direction]",
+ put: "value, [key]",
+ __proto__: null,
+ },
+
+ IDBTransaction: {
+ objectStore: "name",
+ __proto__: null,
+ },
+
+ KeyboardEvent: {
+ initKeyboardEvent: "[type], [canBubble], [cancelable], [view], [keyIdentifier], [location], [ctrlKey], [altKey], [shiftKey], [metaKey], [altGraphKey]",
+ __proto__: null,
+ },
+
+ Location: {
+ assign: "[url]",
+ reload: "[force=false]",
+ replace: "[url]",
+ __proto__: null,
+ },
+
+ MediaController: {
+ /* EventTarget */
+ __proto__: null,
+ },
+
+ MediaControlsHost: {
+ displayNameForTrack: "track",
+ mediaUIImageData: "partID",
+ setSelectedTextTrack: "track",
+ sortedTrackListForMenu: "trackList",
+ __proto__: null,
+ },
+
+ MediaList: {
+ appendMedium: "[newMedium]",
+ deleteMedium: "[oldMedium]",
+ item: "[index]",
+ __proto__: null,
+ },
+
+ MediaQueryList: {
+ addListener: "[listener]",
+ removeListener: "[listener]",
+ __proto__: null,
+ },
+
+ MediaQueryListListener: {
+ queryChanged: "[list]",
+ __proto__: null,
+ },
+
+ MediaSource: {
+ addSourceBuffer: "type",
+ endOfStream: "[error]",
+ removeSourceBuffer: "buffer",
+ __proto__: null,
+ },
+
+ MediaStreamTrack: {
+ applyConstraints: "constraints",
+ __proto__: null,
+ },
+
+ MediaStreamTrackSourcesCallback: {
+ handleEvent: "sources",
+ __proto__: null,
+ },
+
+ MessageEvent: {
+ initMessageEvent: "[typeArg], [canBubbleArg], [cancelableArg], [dataArg], [originArg], [lastEventIdArg], [sourceArg], [messagePorts]",
+ webkitInitMessageEvent: "[typeArg], [canBubbleArg], [cancelableArg], [dataArg], [originArg], [lastEventIdArg], [sourceArg], [transferables]",
+ __proto__: null,
+ },
+
+ MessagePort: {
+ /* EventTarget */
+ __proto__: null,
+ },
+
+ MimeTypeArray: {
+ item: "[index]",
+ namedItem: "[name]",
+ __proto__: null,
+ },
+
+ MouseEvent: {
+ initMouseEvent: "[type], [canBubble], [cancelable], [view], [detail], [screenX], [screenY], [clientX], [clientY], [ctrlKey], [altKey], [shiftKey], [metaKey], [button], [relatedTarget]",
+ __proto__: null,
+ },
+
+ MutationEvent: {
+ initMutationEvent: "[type], [canBubble], [cancelable], [relatedNode], [prevValue], [newValue], [attrName], [attrChange]",
+ __proto__: null,
+ },
+
+ MutationObserver: {
+ observe: "target, options",
+ __proto__: null,
+ },
+
+ NamedNodeMap: {
+ getNamedItem: "[name]",
+ getNamedItemNS: "[namespaceURI], [localName]",
+ item: "[index]",
+ removeNamedItem: "[name]",
+ removeNamedItemNS: "[namespaceURI], [localName]",
+ setNamedItem: "[node]",
+ setNamedItemNS: "[node]",
+ __proto__: null,
+ },
+
+ Navigator: {
+ getUserMedia: "options, successCallback, errorCallback",
+ __proto__: null,
+ },
+
+ NavigatorUserMediaErrorCallback: {
+ handleEvent: "error",
+ __proto__: null,
+ },
+
+ NavigatorUserMediaSuccessCallback: {
+ handleEvent: "stream",
+ __proto__: null,
+ },
+
+ NodeFilter: {
+ acceptNode: "[n]",
+ __proto__: null,
+ },
+
+ NodeList: {
+ item: "index",
+ __proto__: null,
+ },
+
+ Notification: {
+ /* EventTarget */
+ __proto__: null,
+ },
+
+ NotificationCenter: {
+ createNotification: "iconUrl, title, body",
+ requestPermission: "[callback]",
+ __proto__: null,
+ },
+
+ NotificationPermissionCallback: {
+ handleEvent: "permission",
+ __proto__: null,
+ },
+
+ OESVertexArrayObject: {
+ bindVertexArrayOES: "[arrayObject]",
+ deleteVertexArrayOES: "[arrayObject]",
+ isVertexArrayOES: "[arrayObject]",
+ __proto__: null,
+ },
+
+ OscillatorNode: {
+ noteOff: "when",
+ noteOn: "when",
+ setPeriodicWave: "wave",
+ start: "[when]",
+ stop: "[when]",
+ __proto__: null,
+ },
+
+ Path2D: {
+ addPath: "path, [transform]",
+ arc: "[x], [y], [radius], [startAngle], [endAngle], [anticlockwise]",
+ arcTo: "[x1], [y1], [x2], [y2], [radius]",
+ bezierCurveTo: "[cp1x], [cp1y], [cp2x], [cp2y], [x], [y]",
+ ellipse: "x, y, radiusX, radiusY, rotation, startAngle, endAngle, [anticlockwise]",
+ lineTo: "[x], [y]",
+ moveTo: "[x], [y]",
+ quadraticCurveTo: "[cpx], [cpy], [x], [y]",
+ rect: "[x], [y], [width], [height]",
+ __proto__: null,
+ },
+
+ Performance: {
+ clearMarks: "[name]",
+ clearMeasures: "name",
+ getEntriesByName: "name, [type]",
+ getEntriesByType: "type",
+ mark: "name",
+ measure: "name, [startMark], [endMark]",
+ __proto__: null,
+ },
+
+ PerformanceObserver: {
+ observe: "options",
+ __proto__: null,
+ },
+
+ PerformanceObserverEntryList: {
+ getEntriesByName: "name, [type]",
+ getEntriesByType: "type",
+ __proto__: null,
+ },
+
+ Plugin: {
+ item: "[index]",
+ namedItem: "[name]",
+ __proto__: null,
+ },
+
+ PluginArray: {
+ item: "[index]",
+ namedItem: "[name]",
+ refresh: "[reload]",
+ __proto__: null,
+ },
+
+ PositionCallback: {
+ handleEvent: "position",
+ __proto__: null,
+ },
+
+ PositionErrorCallback: {
+ handleEvent: "error",
+ __proto__: null,
+ },
+
+ QuickTimePluginReplacement: {
+ postEvent: "eventName",
+ __proto__: null,
+ },
+
+ RTCDTMFSender: {
+ insertDTMF: "tones, [duration], [interToneGap]",
+ __proto__: null,
+ },
+
+ RTCDataChannel: {
+ send: "data",
+ __proto__: null,
+ },
+
+ RTCPeerConnectionErrorCallback: {
+ handleEvent: "error",
+ __proto__: null,
+ },
+
+ RTCSessionDescriptionCallback: {
+ handleEvent: "sdp",
+ __proto__: null,
+ },
+
+ RTCStatsCallback: {
+ handleEvent: "response",
+ __proto__: null,
+ },
+
+ RTCStatsReport: {
+ stat: "name",
+ __proto__: null,
+ },
+
+ RTCStatsResponse: {
+ namedItem: "[name]",
+ __proto__: null,
+ },
+
+ Range: {
+ collapse: "[toStart]",
+ compareBoundaryPoints: "[how], [sourceRange]",
+ compareNode: "[refNode]",
+ comparePoint: "[refNode], [offset]",
+ createContextualFragment: "[html]",
+ expand: "[unit]",
+ insertNode: "[newNode]",
+ intersectsNode: "[refNode]",
+ isPointInRange: "[refNode], [offset]",
+ selectNode: "[refNode]",
+ selectNodeContents: "[refNode]",
+ setEnd: "[refNode], [offset]",
+ setEndAfter: "[refNode]",
+ setEndBefore: "[refNode]",
+ setStart: "[refNode], [offset]",
+ setStartAfter: "[refNode]",
+ setStartBefore: "[refNode]",
+ surroundContents: "[newParent]",
+ __proto__: null,
+ },
+
+ ReadableStream: {
+ cancel: "reason",
+ pipeThrough: "dest, options",
+ pipeTo: "streams, options",
+ __proto__: null,
+ },
+
+ WritableStream: {
+ abort: "reason",
+ close: "",
+ write: "chunk",
+ __proto__: null,
+ },
+
+ RequestAnimationFrameCallback: {
+ handleEvent: "highResTime",
+ __proto__: null,
+ },
+
+ SQLResultSetRowList: {
+ item: "index",
+ __proto__: null,
+ },
+
+ SQLStatementCallback: {
+ handleEvent: "transaction, resultSet",
+ __proto__: null,
+ },
+
+ SQLStatementErrorCallback: {
+ handleEvent: "transaction, error",
+ __proto__: null,
+ },
+
+ SQLTransaction: {
+ executeSql: "sqlStatement, arguments, [callback], [errorCallback]",
+ __proto__: null,
+ },
+
+ SQLTransactionCallback: {
+ handleEvent: "transaction",
+ __proto__: null,
+ },
+
+ SQLTransactionErrorCallback: {
+ handleEvent: "error",
+ __proto__: null,
+ },
+
+ SVGAngle: {
+ convertToSpecifiedUnits: "unitType",
+ newValueSpecifiedUnits: "unitType, valueInSpecifiedUnits",
+ __proto__: null,
+ },
+
+ SVGAnimationElement: {
+ beginElementAt: "[offset]",
+ endElementAt: "[offset]",
+ hasExtension: "[extension]",
+ __proto__: null,
+ },
+
+ SVGColor: {
+ setColor: "colorType, rgbColor, iccColor",
+ setRGBColor: "rgbColor",
+ setRGBColorICCColor: "rgbColor, iccColor",
+ __proto__: null,
+ },
+
+ SVGCursorElement: {
+ hasExtension: "[extension]",
+ __proto__: null,
+ },
+
+ SVGDocument: {
+ createEvent: "[eventType]",
+ __proto__: null,
+ },
+
+ SVGElement: {
+ getPresentationAttribute: "[name]",
+ __proto__: null,
+ },
+
+ SVGFEDropShadowElement: {
+ setStdDeviation: "[stdDeviationX], [stdDeviationY]",
+ __proto__: null,
+ },
+
+ SVGFEGaussianBlurElement: {
+ setStdDeviation: "[stdDeviationX], [stdDeviationY]",
+ __proto__: null,
+ },
+
+ SVGFEMorphologyElement: {
+ setRadius: "[radiusX], [radiusY]",
+ __proto__: null,
+ },
+
+ SVGFilterElement: {
+ setFilterRes: "[filterResX], [filterResY]",
+ __proto__: null,
+ },
+
+ SVGGraphicsElement: {
+ getTransformToElement: "[element]",
+ hasExtension: "[extension]",
+ __proto__: null,
+ },
+
+ SVGLength: {
+ convertToSpecifiedUnits: "unitType",
+ newValueSpecifiedUnits: "unitType, valueInSpecifiedUnits",
+ __proto__: null,
+ },
+
+ SVGLengthList: {
+ appendItem: "item",
+ getItem: "index",
+ initialize: "item",
+ insertItemBefore: "item, index",
+ removeItem: "index",
+ replaceItem: "item, index",
+ __proto__: null,
+ },
+
+ SVGMarkerElement: {
+ setOrientToAngle: "[angle]",
+ __proto__: null,
+ },
+
+ SVGMaskElement: {
+ hasExtension: "[extension]",
+ __proto__: null,
+ },
+
+ SVGMatrix: {
+ multiply: "secondMatrix",
+ rotate: "angle",
+ rotateFromVector: "x, y",
+ scale: "scaleFactor",
+ scaleNonUniform: "scaleFactorX, scaleFactorY",
+ skewX: "angle",
+ skewY: "angle",
+ translate: "x, y",
+ __proto__: null,
+ },
+
+ SVGNumberList: {
+ appendItem: "item",
+ getItem: "index",
+ initialize: "item",
+ insertItemBefore: "item, index",
+ removeItem: "index",
+ replaceItem: "item, index",
+ __proto__: null,
+ },
+
+ SVGPaint: {
+ setPaint: "paintType, uri, rgbColor, iccColor",
+ setUri: "uri",
+ __proto__: null,
+ },
+
+ SVGPathElement: {
+ createSVGPathSegArcAbs: "[x], [y], [r1], [r2], [angle], [largeArcFlag], [sweepFlag]",
+ createSVGPathSegArcRel: "[x], [y], [r1], [r2], [angle], [largeArcFlag], [sweepFlag]",
+ createSVGPathSegCurvetoCubicAbs: "[x], [y], [x1], [y1], [x2], [y2]",
+ createSVGPathSegCurvetoCubicRel: "[x], [y], [x1], [y1], [x2], [y2]",
+ createSVGPathSegCurvetoCubicSmoothAbs: "[x], [y], [x2], [y2]",
+ createSVGPathSegCurvetoCubicSmoothRel: "[x], [y], [x2], [y2]",
+ createSVGPathSegCurvetoQuadraticAbs: "[x], [y], [x1], [y1]",
+ createSVGPathSegCurvetoQuadraticRel: "[x], [y], [x1], [y1]",
+ createSVGPathSegCurvetoQuadraticSmoothAbs: "[x], [y]",
+ createSVGPathSegCurvetoQuadraticSmoothRel: "[x], [y]",
+ createSVGPathSegLinetoAbs: "[x], [y]",
+ createSVGPathSegLinetoHorizontalAbs: "[x]",
+ createSVGPathSegLinetoHorizontalRel: "[x]",
+ createSVGPathSegLinetoRel: "[x], [y]",
+ createSVGPathSegLinetoVerticalAbs: "[y]",
+ createSVGPathSegLinetoVerticalRel: "[y]",
+ createSVGPathSegMovetoAbs: "[x], [y]",
+ createSVGPathSegMovetoRel: "[x], [y]",
+ getPathSegAtLength: "[distance]",
+ getPointAtLength: "[distance]",
+ __proto__: null,
+ },
+
+ SVGPathSegList: {
+ appendItem: "newItem",
+ getItem: "index",
+ initialize: "newItem",
+ insertItemBefore: "newItem, index",
+ removeItem: "index",
+ replaceItem: "newItem, index",
+ __proto__: null,
+ },
+
+ SVGPatternElement: {
+ hasExtension: "[extension]",
+ __proto__: null,
+ },
+
+ SVGPoint: {
+ matrixTransform: "matrix",
+ __proto__: null,
+ },
+
+ SVGPointList: {
+ appendItem: "item",
+ getItem: "index",
+ initialize: "item",
+ insertItemBefore: "item, index",
+ removeItem: "index",
+ replaceItem: "item, index",
+ __proto__: null,
+ },
+
+ SVGSVGElement: {
+ checkEnclosure: "[element], [rect]",
+ checkIntersection: "[element], [rect]",
+ createSVGTransformFromMatrix: "[matrix]",
+ getElementById: "[elementId]",
+ getEnclosureList: "[rect], [referenceElement]",
+ getIntersectionList: "[rect], [referenceElement]",
+ setCurrentTime: "[seconds]",
+ suspendRedraw: "[maxWaitMilliseconds]",
+ unsuspendRedraw: "[suspendHandleId]",
+ __proto__: null,
+ },
+
+ SVGStringList: {
+ appendItem: "item",
+ getItem: "index",
+ initialize: "item",
+ insertItemBefore: "item, index",
+ removeItem: "index",
+ replaceItem: "item, index",
+ __proto__: null,
+ },
+
+ SVGTextContentElement: {
+ getCharNumAtPosition: "[point]",
+ getEndPositionOfChar: "[offset]",
+ getExtentOfChar: "[offset]",
+ getRotationOfChar: "[offset]",
+ getStartPositionOfChar: "[offset]",
+ getSubStringLength: "[offset], [length]",
+ selectSubString: "[offset], [length]",
+ __proto__: null,
+ },
+
+ SVGTransform: {
+ setMatrix: "matrix",
+ setRotate: "angle, cx, cy",
+ setScale: "sx, sy",
+ setSkewX: "angle",
+ setSkewY: "angle",
+ setTranslate: "tx, ty",
+ __proto__: null,
+ },
+
+ SVGTransformList: {
+ appendItem: "item",
+ createSVGTransformFromMatrix: "matrix",
+ getItem: "index",
+ initialize: "item",
+ insertItemBefore: "item, index",
+ removeItem: "index",
+ replaceItem: "item, index",
+ __proto__: null,
+ },
+
+ SecurityPolicy: {
+ allowsConnectionTo: "url",
+ allowsFontFrom: "url",
+ allowsFormAction: "url",
+ allowsFrameFrom: "url",
+ allowsImageFrom: "url",
+ allowsMediaFrom: "url",
+ allowsObjectFrom: "url",
+ allowsPluginType: "type",
+ allowsScriptFrom: "url",
+ allowsStyleFrom: "url",
+ __proto__: null,
+ },
+
+ Selection: {
+ addRange: "[range]",
+ collapse: "[node], [index]",
+ containsNode: "[node], [allowPartial]",
+ extend: "[node], [offset]",
+ getRangeAt: "[index]",
+ modify: "[alter], [direction], [granularity]",
+ selectAllChildren: "[node]",
+ setBaseAndExtent: "[baseNode], [baseOffset], [extentNode], [extentOffset]",
+ setPosition: "[node], [offset]",
+ __proto__: null,
+ },
+
+ SourceBuffer: {
+ appendBuffer: "data",
+ remove: "start, end",
+ __proto__: null,
+ },
+
+ SourceBufferList: {
+ item: "index",
+ __proto__: null,
+ },
+
+ SpeechSynthesis: {
+ speak: "utterance",
+ __proto__: null,
+ },
+
+ SpeechSynthesisUtterance: {
+ /* EventTarget */
+ __proto__: null,
+ },
+
+ Storage: {
+ getItem: "key",
+ key: "index",
+ removeItem: "key",
+ setItem: "key, data",
+ __proto__: null,
+ },
+
+ StorageErrorCallback: {
+ handleEvent: "error",
+ __proto__: null,
+ },
+
+ StorageEvent: {
+ initStorageEvent: "[typeArg], [canBubbleArg], [cancelableArg], [keyArg], [oldValueArg], [newValueArg], [urlArg], [storageAreaArg]",
+ __proto__: null,
+ },
+
+ StorageInfo: {
+ queryUsageAndQuota: "storageType, [usageCallback], [errorCallback]",
+ requestQuota: "storageType, newQuotaInBytes, [quotaCallback], [errorCallback]",
+ __proto__: null,
+ },
+
+ StorageQuota: {
+ queryUsageAndQuota: "usageCallback, [errorCallback]",
+ requestQuota: "newQuotaInBytes, [quotaCallback], [errorCallback]",
+ __proto__: null,
+ },
+
+ StorageQuotaCallback: {
+ handleEvent: "grantedQuotaInBytes",
+ __proto__: null,
+ },
+
+ StorageUsageCallback: {
+ handleEvent: "currentUsageInBytes, currentQuotaInBytes",
+ __proto__: null,
+ },
+
+ StringCallback: {
+ handleEvent: "data",
+ __proto__: null,
+ },
+
+ StyleMedia: {
+ matchMedium: "[mediaquery]",
+ __proto__: null,
+ },
+
+ StyleSheetList: {
+ item: "[index]",
+ __proto__: null,
+ },
+
+ Text: {
+ replaceWholeText: "[content]",
+ splitText: "offset",
+ __proto__: null,
+ },
+
+ TextEvent: {
+ initTextEvent: "[typeArg], [canBubbleArg], [cancelableArg], [viewArg], [dataArg]",
+ __proto__: null,
+ },
+
+ TextTrack: {
+ addCue: "cue",
+ addRegion: "region",
+ removeCue: "cue",
+ removeRegion: "region",
+ __proto__: null,
+ },
+
+ TextTrackCue: {
+ /* EventTarget */
+ __proto__: null,
+ },
+
+ TextTrackCueList: {
+ getCueById: "id",
+ item: "index",
+ __proto__: null,
+ },
+
+ TextTrackList: {
+ getTrackById: "id",
+ item: "index",
+ __proto__: null,
+ },
+
+ TimeRanges: {
+ end: "index",
+ start: "index",
+ __proto__: null,
+ },
+
+ TouchEvent: {
+ initTouchEvent: "[touches], [targetTouches], [changedTouches], [type], [view], [screenX], [screenY], [clientX], [clientY], [ctrlKey], [altKey], [shiftKey], [metaKey]",
+ __proto__: null,
+ },
+
+ TouchList: {
+ item: "index",
+ __proto__: null,
+ },
+
+ UIEvent: {
+ initUIEvent: "[type], [canBubble], [cancelable], [view], [detail]",
+ __proto__: null,
+ },
+
+ UserMessageHandler: {
+ postMessage: "message",
+ __proto__: null,
+ },
+
+ VTTRegionList: {
+ getRegionById: "id",
+ item: "index",
+ __proto__: null,
+ },
+
+ VideoTrackList: {
+ getTrackById: "id",
+ item: "index",
+ __proto__: null,
+ },
+
+ WebGL2RenderingContext: {
+ beginQuery: "target, query",
+ beginTransformFeedback: "primitiveMode",
+ bindBufferBase: "target, index, buffer",
+ bindBufferRange: "target, index, buffer, offset, size",
+ bindSampler: "unit, sampler",
+ bindTransformFeedback: "target, id",
+ bindVertexArray: "vertexArray",
+ blitFramebuffer: "srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter",
+ clearBufferfi: "buffer, drawbuffer, depth, stencil",
+ clearBufferfv: "buffer, drawbuffer, value",
+ clearBufferiv: "buffer, drawbuffer, value",
+ clearBufferuiv: "buffer, drawbuffer, value",
+ clientWaitSync: "sync, flags, timeout",
+ compressedTexImage3D: "target, level, internalformat, width, height, depth, border, imageSize, data",
+ compressedTexSubImage3D: "target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data",
+ copyBufferSubData: "readTarget, writeTarget, readOffset, writeOffset, size",
+ copyTexSubImage3D: "target, level, xoffset, yoffset, zoffset, x, y, width, height",
+ deleteQuery: "query",
+ deleteSampler: "sampler",
+ deleteSync: "sync",
+ deleteTransformFeedback: "id",
+ deleteVertexArray: "vertexArray",
+ drawArraysInstanced: "mode, first, count, instanceCount",
+ drawBuffers: "buffers",
+ drawElementsInstanced: "mode, count, type, offset, instanceCount",
+ drawRangeElements: "mode, start, end, count, type, offset",
+ endQuery: "target",
+ fenceSync: "condition, flags",
+ framebufferTextureLayer: "target, attachment, texture, level, layer",
+ getActiveUniformBlockName: "program, uniformBlockIndex",
+ getActiveUniformBlockParameter: "program, uniformBlockIndex, pname",
+ getActiveUniforms: "program, uniformIndices, pname",
+ getBufferSubData: "target, offset, returnedData",
+ getFragDataLocation: "program, name",
+ getIndexedParameter: "target, index",
+ getInternalformatParameter: "target, internalformat, pname",
+ getQuery: "target, pname",
+ getQueryParameter: "query, pname",
+ getSamplerParameter: "sampler, pname",
+ getSyncParameter: "sync, pname",
+ getTransformFeedbackVarying: "program, index",
+ getUniformBlockIndex: "program, uniformBlockName",
+ getUniformIndices: "program, uniformNames",
+ invalidateFramebuffer: "target, attachments",
+ invalidateSubFramebuffer: "target, attachments, x, y, width, height",
+ isQuery: "query",
+ isSampler: "sampler",
+ isSync: "sync",
+ isTransformFeedback: "id",
+ isVertexArray: "vertexArray",
+ readBuffer: "src",
+ renderbufferStorageMultisample: "target, samples, internalformat, width, height",
+ samplerParameterf: "sampler, pname, param",
+ samplerParameteri: "sampler, pname, param",
+ texImage3D: "target, level, internalformat, width, height, depth, border, format, type, pixels",
+ texStorage2D: "target, levels, internalformat, width, height",
+ texStorage3D: "target, levels, internalformat, width, height, depth",
+ texSubImage3D: "target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels",
+ transformFeedbackVaryings: "program, varyings, bufferMode",
+ uniform1ui: "location, v0",
+ uniform1uiv: "location, value",
+ uniform2ui: "location, v0, v1",
+ uniform2uiv: "location, value",
+ uniform3ui: "location, v0, v1, v2",
+ uniform3uiv: "location, value",
+ uniform4ui: "location, v0, v1, v2, v3",
+ uniform4uiv: "location, value",
+ uniformBlockBinding: "program, uniformBlockIndex, uniformBlockBinding",
+ uniformMatrix2x3fv: "location, transpose, value",
+ uniformMatrix2x4fv: "location, transpose, value",
+ uniformMatrix3x2fv: "location, transpose, value",
+ uniformMatrix3x4fv: "location, transpose, value",
+ uniformMatrix4x2fv: "location, transpose, value",
+ uniformMatrix4x3fv: "location, transpose, value",
+ vertexAttribDivisor: "index, divisor",
+ vertexAttribI4i: "index, x, y, z, w",
+ vertexAttribI4iv: "index, v",
+ vertexAttribI4ui: "index, x, y, z, w",
+ vertexAttribI4uiv: "index, v",
+ vertexAttribIPointer: "index, size, type, stride, offset",
+ waitSync: "sync, flags, timeout",
+ __proto__: null,
+ },
+
+ WebGLDebugShaders: {
+ getTranslatedShaderSource: "shader",
+ __proto__: null,
+ },
+
+ WebGLDrawBuffers: {
+ drawBuffersWEBGL: "buffers",
+ __proto__: null,
+ },
+
+ WebGLRenderingContextBase: {
+ activeTexture: "texture",
+ attachShader: "program, shader",
+ bindAttribLocation: "program, index, name",
+ bindBuffer: "target, buffer",
+ bindFramebuffer: "target, framebuffer",
+ bindRenderbuffer: "target, renderbuffer",
+ bindTexture: "target, texture",
+ blendColor: "red, green, blue, alpha",
+ blendEquation: "mode",
+ blendEquationSeparate: "modeRGB, modeAlpha",
+ blendFunc: "sfactor, dfactor",
+ blendFuncSeparate: "srcRGB, dstRGB, srcAlpha, dstAlpha",
+ bufferData: "target, data, usage",
+ bufferSubData: "target, offset, data",
+ checkFramebufferStatus: "target",
+ clear: "mask",
+ clearColor: "red, green, blue, alpha",
+ clearDepth: "depth",
+ clearStencil: "s",
+ colorMask: "red, green, blue, alpha",
+ compileShader: "shader",
+ compressedTexImage2D: "target, level, internalformat, width, height, border, data",
+ compressedTexSubImage2D: "target, level, xoffset, yoffset, width, height, format, data",
+ copyTexImage2D: "target, level, internalformat, x, y, width, height, border",
+ copyTexSubImage2D: "target, level, xoffset, yoffset, x, y, width, height",
+ createShader: "type",
+ cullFace: "mode",
+ deleteBuffer: "buffer",
+ deleteFramebuffer: "framebuffer",
+ deleteProgram: "program",
+ deleteRenderbuffer: "renderbuffer",
+ deleteShader: "shader",
+ deleteTexture: "texture",
+ depthFunc: "func",
+ depthMask: "flag",
+ depthRange: "zNear, zFar",
+ detachShader: "program, shader",
+ disable: "cap",
+ disableVertexAttribArray: "index",
+ drawArrays: "mode, first, count",
+ drawElements: "mode, count, type, offset",
+ enable: "cap",
+ enableVertexAttribArray: "index",
+ framebufferRenderbuffer: "target, attachment, renderbuffertarget, renderbuffer",
+ framebufferTexture2D: "target, attachment, textarget, texture, level",
+ frontFace: "mode",
+ generateMipmap: "target",
+ getActiveAttrib: "program, index",
+ getActiveUniform: "program, index",
+ getAttachedShaders: "program",
+ getAttribLocation: "program, name",
+ getBufferParameter: "target, pname",
+ getExtension: "name",
+ getFramebufferAttachmentParameter: "target, attachment, pname",
+ getParameter: "pname",
+ getProgramInfoLog: "program",
+ getProgramParameter: "program, pname",
+ getRenderbufferParameter: "target, pname",
+ getShaderInfoLog: "shader",
+ getShaderParameter: "shader, pname",
+ getShaderPrecisionFormat: "shadertype, precisiontype",
+ getShaderSource: "shader",
+ getTexParameter: "target, pname",
+ getUniform: "program, location",
+ getUniformLocation: "program, name",
+ getVertexAttrib: "index, pname",
+ getVertexAttribOffset: "index, pname",
+ hint: "target, mode",
+ isBuffer: "buffer",
+ isEnabled: "cap",
+ isFramebuffer: "framebuffer",
+ isProgram: "program",
+ isRenderbuffer: "renderbuffer",
+ isShader: "shader",
+ isTexture: "texture",
+ lineWidth: "width",
+ linkProgram: "program",
+ pixelStorei: "pname, param",
+ polygonOffset: "factor, units",
+ readPixels: "x, y, width, height, format, type, pixels",
+ renderbufferStorage: "target, internalformat, width, height",
+ sampleCoverage: "value, invert",
+ scissor: "x, y, width, height",
+ shaderSource: "shader, string",
+ stencilFunc: "func, ref, mask",
+ stencilFuncSeparate: "face, func, ref, mask",
+ stencilMask: "mask",
+ stencilMaskSeparate: "face, mask",
+ stencilOp: "fail, zfail, zpass",
+ stencilOpSeparate: "face, fail, zfail, zpass",
+ texImage2D: "target, level, internalformat, width, height, border, format, type, pixels",
+ texParameterf: "target, pname, param",
+ texParameteri: "target, pname, param",
+ texSubImage2D: "target, level, xoffset, yoffset, width, height, format, type, pixels",
+ uniform1f: "location, x",
+ uniform1fv: "location, v",
+ uniform1i: "location, x",
+ uniform1iv: "location, v",
+ uniform2f: "location, x, y",
+ uniform2fv: "location, v",
+ uniform2i: "location, x, y",
+ uniform2iv: "location, v",
+ uniform3f: "location, x, y, z",
+ uniform3fv: "location, v",
+ uniform3i: "location, x, y, z",
+ uniform3iv: "location, v",
+ uniform4f: "location, x, y, z, w",
+ uniform4fv: "location, v",
+ uniform4i: "location, x, y, z, w",
+ uniform4iv: "location, v",
+ uniformMatrix2fv: "location, transpose, array",
+ uniformMatrix3fv: "location, transpose, array",
+ uniformMatrix4fv: "location, transpose, array",
+ useProgram: "program",
+ validateProgram: "program",
+ vertexAttrib1f: "indx, x",
+ vertexAttrib1fv: "indx, values",
+ vertexAttrib2f: "indx, x, y",
+ vertexAttrib2fv: "indx, values",
+ vertexAttrib3f: "indx, x, y, z",
+ vertexAttrib3fv: "indx, values",
+ vertexAttrib4f: "indx, x, y, z, w",
+ vertexAttrib4fv: "indx, values",
+ vertexAttribPointer: "indx, size, type, normalized, stride, offset",
+ viewport: "x, y, width, height",
+ __proto__: null,
+ },
+
+ WebKitCSSMatrix: {
+ multiply: "[secondMatrix]",
+ rotate: "[rotX], [rotY], [rotZ]",
+ rotateAxisAngle: "[x], [y], [z], [angle]",
+ scale: "[scaleX], [scaleY], [scaleZ]",
+ setMatrixValue: "[string]",
+ skewX: "[angle]",
+ skewY: "[angle]",
+ translate: "[x], [y], [z]",
+ __proto__: null,
+ },
+
+ WebKitMediaKeySession: {
+ update: "key",
+ __proto__: null,
+ },
+
+ WebKitMediaKeys: {
+ createSession: "[type], [initData]",
+ __proto__: null,
+ },
+
+ WebKitNamedFlow: {
+ getRegionsByContent: "contentNode",
+ __proto__: null,
+ },
+
+ WebKitNamedFlowCollection: {
+ item: "index",
+ namedItem: "name",
+ __proto__: null,
+ },
+
+ WebKitSubtleCrypto: {
+ decrypt: "algorithm, key, data",
+ digest: "algorithm, data",
+ encrypt: "algorithm, key, data",
+ exportKey: "format, key",
+ generateKey: "algorithm, [extractable], [keyUsages]",
+ importKey: "format, keyData, algorithm, [extractable], [keyUsages]",
+ sign: "algorithm, key, data",
+ unwrapKey: "format, wrappedKey, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, [extractable], [keyUsages]",
+ verify: "algorithm, key, signature, data",
+ wrapKey: "format, key, wrappingKey, wrapAlgorithm",
+ __proto__: null,
+ },
+
+ WebSocket: {
+ close: "[code], [reason]",
+ send: "data",
+ __proto__: null,
+ },
+
+ WheelEvent: {
+ initWebKitWheelEvent: "[wheelDeltaX], [wheelDeltaY], [view], [screenX], [screenY], [clientX], [clientY], [ctrlKey], [altKey], [shiftKey], [metaKey]",
+ __proto__: null,
+ },
+
+ Worker: {
+ postMessage: "message, [messagePorts]",
+ __proto__: null,
+ },
+
+ WorkerGlobalScope: {
+ clearInterval: "[handle]",
+ clearTimeout: "[handle]",
+ setInterval: "handler, [timeout]",
+ setTimeout: "handler, [timeout]",
+ __proto__: null,
+ },
+
+ XMLHttpRequest: {
+ getResponseHeader: "header",
+ open: "method, url, [async], [user], [password]",
+ overrideMimeType: "override",
+ setRequestHeader: "header, value",
+ __proto__: null,
+ },
+
+ XMLHttpRequestUpload: {
+ /* EventTarget */
+ __proto__: null,
+ },
+
+ XMLSerializer: {
+ serializeToString: "[node]",
+ __proto__: null,
+ },
+
+ XPathEvaluator: {
+ createExpression: "[expression], [resolver]",
+ createNSResolver: "[nodeResolver]",
+ evaluate: "[expression], [contextNode], [resolver], [type], [inResult]",
+ __proto__: null,
+ },
+
+ XPathExpression: {
+ evaluate: "[contextNode], [type], [inResult]",
+ __proto__: null,
+ },
+
+ XPathNSResolver: {
+ lookupNamespaceURI: "[prefix]",
+ __proto__: null,
+ },
+
+ XPathResult: {
+ snapshotItem: "[index]",
+ __proto__: null,
+ },
+
+ XSLTProcessor: {
+ getParameter: "namespaceURI, localName",
+ importStylesheet: "[stylesheet]",
+ removeParameter: "namespaceURI, localName",
+ setParameter: "namespaceURI, localName, value",
+ transformToDocument: "[source]",
+ transformToFragment: "[source], [docVal]",
+ __proto__: null,
+ },
+
+ webkitAudioContext: {
+ createBuffer: "numberOfChannels, numberOfFrames, sampleRate",
+ createChannelMerger: "[numberOfInputs]",
+ createChannelSplitter: "[numberOfOutputs]",
+ createDelay: "[maxDelayTime]",
+ createDelayNode: "[maxDelayTime]",
+ createJavaScriptNode: "bufferSize, [numberOfInputChannels], [numberOfOutputChannels]",
+ createMediaElementSource: "mediaElement",
+ createPeriodicWave: "real, imag",
+ createScriptProcessor: "bufferSize, [numberOfInputChannels], [numberOfOutputChannels]",
+ decodeAudioData: "audioData, successCallback, [errorCallback]",
+ __proto__: null,
+ },
+
+ webkitAudioPannerNode: {
+ setOrientation: "x, y, z",
+ setPosition: "x, y, z",
+ setVelocity: "x, y, z",
+ __proto__: null,
+ },
+
+ webkitMediaStream: {
+ addTrack: "track",
+ getTrackById: "trackId",
+ removeTrack: "track",
+ __proto__: null,
+ },
+
+ webkitRTCPeerConnection: {
+ addIceCandidate: "candidate, successCallback, failureCallback",
+ addStream: "stream",
+ createAnswer: "successCallback, failureCallback, [answerOptions]",
+ createDTMFSender: "track",
+ createDataChannel: "label, [options]",
+ createOffer: "successCallback, failureCallback, [offerOptions]",
+ getStats: "successCallback, failureCallback, [selector]",
+ getStreamById: "streamId",
+ removeStream: "stream",
+ setLocalDescription: "description, successCallback, failureCallback",
+ setRemoteDescription: "description, successCallback, failureCallback",
+ updateIce: "configuration",
+ __proto__: null,
+ },
+
+ EventTarget: {
+ addEventListener: "type, listener, [useCapture=false]",
+ removeEventListener: "type, listener, [useCapture=false]",
+ dispatchEvent: "event",
+ __proto__: null,
+ },
+};
+
+(function() {
+ // COMPATIBILITY (iOS 9): EventTarget properties were on instances, now there
+ // is an actual EventTarget prototype in the chain.
+ var EventTarget = WebInspector.NativePrototypeFunctionParameters.EventTarget;
+ var eventTargetTypes = [
+ "Node", "Window",
+ "AudioNode", "AudioTrackList", "DOMApplicationCache", "FileReader",
+ "MediaController", "MediaStreamTrack", "MessagePort", "Notification", "RTCDTMFSender",
+ "SpeechSynthesisUtterance", "TextTrack", "TextTrackCue", "TextTrackList",
+ "VideoTrackList", "WebKitMediaKeySession", "WebKitNamedFlow", "WebSocket",
+ "WorkerGlobalScope", "XMLHttpRequest", "webkitMediaStream", "webkitRTCPeerConnection"
+ ];
+ for (var type of eventTargetTypes)
+ Object.assign(WebInspector.NativePrototypeFunctionParameters[type], EventTarget);
+
+ var ElementQueries = {
+ getElementsByClassName: "classNames",
+ getElementsByTagName: "tagName",
+ getElementsByTagNameNS: "namespace, localName",
+ querySelector: "selectors",
+ querySelectorAll: "selectors",
+ };
+ Object.assign(WebInspector.NativePrototypeFunctionParameters.Element, ElementQueries);
+ Object.assign(WebInspector.NativePrototypeFunctionParameters.Document, ElementQueries);
+
+ var ChildNode = {
+ after: "[node|string]...",
+ before: "[node|string]...",
+ replaceWith: "[node|string]...",
+ };
+ Object.assign(WebInspector.NativePrototypeFunctionParameters.Element, ChildNode);
+ Object.assign(WebInspector.NativePrototypeFunctionParameters.CharacterData, ChildNode);
+
+ var ParentNode = {
+ append: "[node|string]...",
+ prepend: "[node|string]...",
+ };
+ Object.assign(WebInspector.NativePrototypeFunctionParameters.Element, ParentNode);
+ Object.assign(WebInspector.NativePrototypeFunctionParameters.Document, ParentNode);
+ Object.assign(WebInspector.NativePrototypeFunctionParameters.DocumentFragment, ParentNode);
+
+ // COMPATIBILITY (iOS 9): window.console used to be a Console object instance,
+ // now it is just a namespace object on the global object.
+ WebInspector.NativePrototypeFunctionParameters.Console = WebInspector.NativeConstructorFunctionParameters.Console;
+
+})();
diff --git a/Source/WebInspectorUI/UserInterface/Models/NetworkInstrument.js b/Source/WebInspectorUI/UserInterface/Models/NetworkInstrument.js
new file mode 100644
index 000000000..ad8f990f8
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/NetworkInstrument.js
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.NetworkInstrument = class NetworkInstrument extends WebInspector.Instrument
+{
+ // Protected
+
+ get timelineRecordType()
+ {
+ return WebInspector.TimelineRecord.Type.Network;
+ }
+
+ startInstrumentation(initiatedByBackend)
+ {
+ // Nothing to do, network instrumentation is always happening.
+ }
+
+ stopInstrumentation(initiatedByBackend)
+ {
+ // Nothing to do, network instrumentation is always happening.
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/NetworkTimeline.js b/Source/WebInspectorUI/UserInterface/Models/NetworkTimeline.js
new file mode 100644
index 000000000..450ff2c5f
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/NetworkTimeline.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.NetworkTimeline = class NetworkTimeline extends WebInspector.Timeline
+{
+ // Public
+
+ recordForResource(resource)
+ {
+ console.assert(resource instanceof WebInspector.Resource);
+
+ return this._resourceRecordMap.get(resource) || null;
+ }
+
+ reset(suppressEvents)
+ {
+ this._resourceRecordMap = new Map;
+
+ super.reset(suppressEvents);
+ }
+
+ addRecord(record)
+ {
+ console.assert(record instanceof WebInspector.ResourceTimelineRecord);
+
+ // Don't allow duplicate records for a resource.
+ if (this._resourceRecordMap.has(record.resource))
+ return;
+
+ this._resourceRecordMap.set(record.resource, record);
+
+ super.addRecord(record);
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ObjectPreview.js b/Source/WebInspectorUI/UserInterface/Models/ObjectPreview.js
new file mode 100644
index 000000000..f8d0d8328
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ObjectPreview.js
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ObjectPreview = class ObjectPreview extends WebInspector.Object
+{
+ constructor(type, subtype, description, lossless, overflow, properties, entries, size)
+ {
+ super();
+
+ console.assert(type);
+ console.assert(typeof lossless === "boolean");
+ console.assert(!properties || !properties.length || properties[0] instanceof WebInspector.PropertyPreview);
+ console.assert(!entries || !entries.length || entries[0] instanceof WebInspector.CollectionEntryPreview);
+
+ this._type = type;
+ this._subtype = subtype;
+ this._description = description || "";
+ this._lossless = lossless;
+ this._overflow = overflow || false;
+ this._size = size;
+
+ this._properties = properties || null;
+ this._entries = entries || null;
+ }
+
+ // Static
+
+ // Runtime.ObjectPreview.
+ static fromPayload(payload)
+ {
+ if (payload.properties)
+ payload.properties = payload.properties.map(WebInspector.PropertyPreview.fromPayload);
+ if (payload.entries)
+ payload.entries = payload.entries.map(WebInspector.CollectionEntryPreview.fromPayload);
+
+ if (payload.subtype === "array") {
+ // COMPATIBILITY (iOS 8): Runtime.ObjectPreview did not have size property,
+ // instead it was tacked onto the end of the description, like "Array[#]".
+ var match = payload.description.match(/\[(\d+)\]$/);
+ if (match) {
+ payload.size = parseInt(match[1]);
+ payload.description = payload.description.replace(/\[\d+\]$/, "");
+ }
+ }
+
+ return new WebInspector.ObjectPreview(payload.type, payload.subtype, payload.description, payload.lossless, payload.overflow, payload.properties, payload.entries, payload.size);
+ }
+
+ // Public
+
+ get type() { return this._type; }
+ get subtype() { return this._subtype; }
+ get description() { return this._description; }
+ get lossless() { return this._lossless; }
+ get overflow() { return this._overflow; }
+ get propertyPreviews() { return this._properties; }
+ get collectionEntryPreviews() { return this._entries; }
+ get size() { return this._size; }
+
+ hasSize()
+ {
+ return this._size !== undefined && (this._subtype === "array" || this._subtype === "set" || this._subtype === "map" || this._subtype === "weakmap" || this._subtype === "weakset");
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Probe.js b/Source/WebInspectorUI/UserInterface/Models/Probe.js
new file mode 100644
index 000000000..a6ee37af5
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Probe.js
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ProbeSample = class ProbeSample extends WebInspector.Object
+{
+ constructor(sampleId, batchId, elapsedTime, object)
+ {
+ super();
+
+ console.assert(object instanceof WebInspector.RemoteObject);
+
+ this.sampleId = sampleId;
+ this.batchId = batchId;
+ this.timestamp = elapsedTime;
+ this.object = object;
+ }
+};
+
+WebInspector.Probe = class Probe extends WebInspector.Object
+{
+ constructor(id, breakpoint, expression)
+ {
+ super();
+
+ console.assert(id);
+ console.assert(breakpoint instanceof WebInspector.Breakpoint);
+
+ this._id = id;
+ this._breakpoint = breakpoint;
+ this._expression = expression;
+ this._samples = [];
+ }
+
+ // Public
+
+ get id()
+ {
+ return this._id;
+ }
+
+ get breakpoint()
+ {
+ return this._breakpoint;
+ }
+
+ get expression()
+ {
+ return this._expression;
+ }
+
+ set expression(value)
+ {
+ if (this._expression === value)
+ return;
+
+ var data = {oldValue: this._expression, newValue: value};
+ this._expression = value;
+ this.clearSamples();
+ this.dispatchEventToListeners(WebInspector.Probe.Event.ExpressionChanged, data);
+ }
+
+ get samples()
+ {
+ return this._samples.slice();
+ }
+
+ clearSamples()
+ {
+ this._samples = [];
+ this.dispatchEventToListeners(WebInspector.Probe.Event.SamplesCleared);
+ }
+
+ addSample(sample)
+ {
+ console.assert(sample instanceof WebInspector.ProbeSample, "Wrong object type passed as probe sample: ", sample);
+ this._samples.push(sample);
+ this.dispatchEventToListeners(WebInspector.Probe.Event.SampleAdded, sample);
+ }
+};
+
+WebInspector.Probe.Event = {
+ ExpressionChanged: "probe-object-expression-changed",
+ SampleAdded: "probe-object-sample-added",
+ SamplesCleared: "probe-object-samples-cleared"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ProbeSet.js b/Source/WebInspectorUI/UserInterface/Models/ProbeSet.js
new file mode 100644
index 000000000..bf7f4be46
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ProbeSet.js
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// A ProbeSet clusters Probes from the same Breakpoint and their samples.
+
+WebInspector.ProbeSet = class ProbeSet extends WebInspector.Object
+{
+ constructor(breakpoint)
+ {
+ super();
+
+ console.assert(breakpoint instanceof WebInspector.Breakpoint, "Unknown breakpoint argument: ", breakpoint);
+
+ this._breakpoint = breakpoint;
+ this._probes = [];
+ this._probesByIdentifier = new Map;
+
+ this._createDataTable();
+
+ WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceChanged, this);
+ WebInspector.Probe.addEventListener(WebInspector.Probe.Event.SampleAdded, this._sampleCollected, this);
+ WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._breakpointResolvedStateDidChange, this);
+ }
+
+ // Public
+
+ get breakpoint() { return this._breakpoint; }
+ get probes() { return this._probes.slice(); }
+ get dataTable() { return this._dataTable; }
+
+ clear()
+ {
+ this._breakpoint.clearActions(WebInspector.BreakpointAction.Type.Probe);
+ }
+
+ clearSamples()
+ {
+ for (var probe of this._probes)
+ probe.clearSamples();
+
+ var oldTable = this._dataTable;
+ this._createDataTable();
+ this.dispatchEventToListeners(WebInspector.ProbeSet.Event.SamplesCleared, {oldTable});
+ }
+
+ createProbe(expression)
+ {
+ this.breakpoint.createAction(WebInspector.BreakpointAction.Type.Probe, null, expression);
+ }
+
+ addProbe(probe)
+ {
+ console.assert(probe instanceof WebInspector.Probe, "Tried to add non-probe ", probe, " to probe group", this);
+ console.assert(probe.breakpoint === this.breakpoint, "Probe and ProbeSet must have same breakpoint.", probe, this);
+
+ this._probes.push(probe);
+ this._probesByIdentifier.set(probe.id, probe);
+
+ this.dataTable.addProbe(probe);
+ this.dispatchEventToListeners(WebInspector.ProbeSet.Event.ProbeAdded, probe);
+ }
+
+ removeProbe(probe)
+ {
+ console.assert(probe instanceof WebInspector.Probe, "Tried to remove non-probe ", probe, " to probe group", this);
+ console.assert(this._probes.indexOf(probe) !== -1, "Tried to remove probe", probe, " not in group ", this);
+ console.assert(this._probesByIdentifier.has(probe.id), "Tried to remove probe", probe, " not in group ", this);
+
+ this._probes.splice(this._probes.indexOf(probe), 1);
+ this._probesByIdentifier.delete(probe.id);
+ this.dataTable.removeProbe(probe);
+ this.dispatchEventToListeners(WebInspector.ProbeSet.Event.ProbeRemoved, probe);
+ }
+
+ willRemove()
+ {
+ console.assert(!this._probes.length, "ProbeSet.willRemove called, but probes still associated with group: ", this._probes);
+
+ WebInspector.Frame.removeEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceChanged, this);
+ WebInspector.Probe.removeEventListener(WebInspector.Probe.Event.SampleAdded, this._sampleCollected, this);
+ WebInspector.Breakpoint.removeEventListener(WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._breakpointResolvedStateDidChange, this);
+ }
+
+ // Private
+
+ _mainResourceChanged()
+ {
+ this.dataTable.mainResourceChanged();
+ }
+
+ _createDataTable()
+ {
+ if (this.dataTable)
+ this.dataTable.willRemove();
+
+ this._dataTable = new WebInspector.ProbeSetDataTable(this);
+ }
+
+ _sampleCollected(event)
+ {
+ var sample = event.data;
+ console.assert(sample instanceof WebInspector.ProbeSample, "Tried to add non-sample to probe group: ", sample);
+
+ var probe = event.target;
+ if (!this._probesByIdentifier.has(probe.id))
+ return;
+
+ console.assert(this.dataTable);
+ this.dataTable.addSampleForProbe(probe, sample);
+ this.dispatchEventToListeners(WebInspector.ProbeSet.Event.SampleAdded, {probe, sample});
+ }
+
+ _breakpointResolvedStateDidChange(event)
+ {
+ this.dispatchEventToListeners(WebInspector.ProbeSet.Event.ResolvedStateDidChange);
+ }
+};
+
+WebInspector.ProbeSet.Event = {
+ ProbeAdded: "probe-set-probe-added",
+ ProbeRemoved: "probe-set-probe-removed",
+ ResolvedStateDidChange: "probe-set-resolved-state-did-change",
+ SampleAdded: "probe-set-sample-added",
+ SamplesCleared: "probe-set-samples-cleared",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ProbeSetDataFrame.js b/Source/WebInspectorUI/UserInterface/Models/ProbeSetDataFrame.js
new file mode 100644
index 000000000..35c13fe6a
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ProbeSetDataFrame.js
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ProbeSetDataFrame = class ProbeSetDataFrame extends WebInspector.Object
+{
+ constructor(index)
+ {
+ super();
+
+ this._count = 0;
+ this._index = index;
+ this._separator = false;
+ }
+
+ // Static
+
+ static compare(a, b)
+ {
+ console.assert(a instanceof WebInspector.ProbeSetDataFrame, a);
+ console.assert(b instanceof WebInspector.ProbeSetDataFrame, b);
+
+ return a.index - b.index;
+ }
+
+ // Public
+
+ get key()
+ {
+ return String(this._index);
+ }
+
+ get count()
+ {
+ return this._count;
+ }
+
+ get index()
+ {
+ return this._index;
+ }
+
+ get isSeparator()
+ {
+ return this._separator;
+ }
+
+ // The last data frame before a main frame navigation is marked as a "separator" frame.
+ set isSeparator(value)
+ {
+ this._separator = !!value;
+ }
+
+ addSampleForProbe(probe, sample)
+ {
+ this[probe.id] = sample;
+ this._count++;
+ }
+
+ missingKeys(probeSet)
+ {
+ return probeSet.probes.filter(function(probe) {
+ return !this.hasOwnProperty(probe.id);
+ }, this);
+ }
+
+ isComplete(probeSet)
+ {
+ return !this.missingKeys(probeSet).length;
+ }
+
+ fillMissingValues(probeSet)
+ {
+ for (var key of this.missingKeys(probeSet))
+ this[key] = WebInspector.ProbeSetDataFrame.MissingValue;
+ }
+};
+
+WebInspector.ProbeSetDataFrame.MissingValue = "?";
diff --git a/Source/WebInspectorUI/UserInterface/Models/ProbeSetDataTable.js b/Source/WebInspectorUI/UserInterface/Models/ProbeSetDataTable.js
new file mode 100644
index 000000000..4dcee25f8
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ProbeSetDataTable.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ProbeSetDataTable = class ProbeSetDataTable extends WebInspector.Object
+{
+ constructor(probeSet)
+ {
+ super();
+
+ this._probeSet = probeSet;
+ this._frames = [];
+ this._previousBatchIdentifier = WebInspector.ProbeSetDataTable.SentinelValue;
+ }
+
+ // Public
+
+ get frames()
+ {
+ return this._frames.slice();
+ }
+
+ get separators()
+ {
+ return this._frames.filter(function(frame) { return frame.isSeparator; });
+ }
+
+ willRemove()
+ {
+ this.dispatchEventToListeners(WebInspector.ProbeSetDataTable.Event.WillRemove);
+ this._frames = [];
+ delete this._probeSet;
+ }
+
+ mainResourceChanged()
+ {
+ this.addSeparator();
+ }
+
+ addSampleForProbe(probe, sample)
+ {
+ // Eagerly save the frame if the batch identifier differs, or we know the frame is full.
+ // Create a new frame when the batch identifier differs.
+ if (sample.batchId !== this._previousBatchIdentifier) {
+ if (this._openFrame) {
+ this._openFrame.fillMissingValues(this._probeSet);
+ this.addFrame(this._openFrame);
+ }
+ this._openFrame = this.createFrame();
+ this._previousBatchIdentifier = sample.batchId;
+ }
+
+ console.assert(this._openFrame, "Should always have an open frame before adding sample.", this, probe, sample);
+ this._openFrame.addSampleForProbe(probe, sample);
+ if (this._openFrame.count === this._probeSet.probes.length) {
+ this.addFrame(this._openFrame);
+ this._openFrame = null;
+ }
+ }
+
+ addProbe(probe)
+ {
+ for (var frame of this.frames)
+ if (!frame[probe.id])
+ frame[probe.id] = WebInspector.ProbeSetDataTable.UnknownValue;
+ }
+
+ removeProbe(probe)
+ {
+ for (var frame of this.frames)
+ delete frame[probe.id];
+ }
+
+ // Protected - can be overridden by subclasses.
+
+ createFrame()
+ {
+ return new WebInspector.ProbeSetDataFrame(this._frames.length);
+ }
+
+ addFrame(frame)
+ {
+ this._frames.push(frame);
+ this.dispatchEventToListeners(WebInspector.ProbeSetDataTable.Event.FrameInserted, frame);
+ }
+
+ addSeparator()
+ {
+ // Separators must be associated with a frame.
+ if (!this._frames.length)
+ return;
+
+ var previousFrame = this._frames.lastValue;
+ // Don't send out duplicate events for adjacent separators.
+ if (previousFrame.isSeparator)
+ return;
+
+ previousFrame.isSeparator = true;
+ this.dispatchEventToListeners(WebInspector.ProbeSetDataTable.Event.SeparatorInserted, previousFrame);
+ }
+};
+
+WebInspector.ProbeSetDataTable.Event = {
+ FrameInserted: "probe-set-data-table-frame-inserted",
+ SeparatorInserted: "probe-set-data-table-separator-inserted",
+ WillRemove: "probe-set-data-table-will-remove"
+};
+
+WebInspector.ProbeSetDataTable.SentinelValue = -1;
+WebInspector.ProbeSetDataTable.UnknownValue = "?";
diff --git a/Source/WebInspectorUI/UserInterface/Models/Profile.js b/Source/WebInspectorUI/UserInterface/Models/Profile.js
new file mode 100644
index 000000000..b927c9903
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Profile.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Profile = class Profile extends WebInspector.Object
+{
+ constructor(topDownRootNodes)
+ {
+ super();
+
+ topDownRootNodes = topDownRootNodes || [];
+
+ console.assert(topDownRootNodes instanceof Array);
+ console.assert(topDownRootNodes.reduce(function(previousValue, node) { return previousValue && node instanceof WebInspector.ProfileNode; }, true));
+
+ this._topDownRootNodes = topDownRootNodes;
+ }
+
+ // Public
+
+ get topDownRootNodes()
+ {
+ return this._topDownRootNodes;
+ }
+
+ get bottomUpRootNodes()
+ {
+ // FIXME: Implement.
+ return [];
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ProfileNode.js b/Source/WebInspectorUI/UserInterface/Models/ProfileNode.js
new file mode 100644
index 000000000..e6e952245
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ProfileNode.js
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ProfileNode = class ProfileNode extends WebInspector.Object
+{
+ constructor(id, type, functionName, sourceCodeLocation, callInfo, calls, childNodes)
+ {
+ super();
+
+ childNodes = childNodes || [];
+
+ console.assert(id);
+ console.assert(!calls || calls instanceof Array);
+ console.assert(!calls || calls.length >= 1);
+ console.assert(!calls || calls.every((call) => call instanceof WebInspector.ProfileNodeCall));
+ console.assert(childNodes instanceof Array);
+ console.assert(childNodes.every((node) => node instanceof WebInspector.ProfileNode));
+
+ this._id = id;
+ this._type = type || WebInspector.ProfileNode.Type.Function;
+ this._functionName = functionName || null;
+ this._sourceCodeLocation = sourceCodeLocation || null;
+ this._calls = calls || null;
+ this._callInfo = callInfo || null;
+ this._childNodes = childNodes;
+ this._parentNode = null;
+ this._previousSibling = null;
+ this._nextSibling = null;
+ this._computedTotalTimes = false;
+
+ if (this._callInfo) {
+ this._startTime = this._callInfo.startTime;
+ this._endTime = this._callInfo.endTime;
+ this._totalTime = this._callInfo.totalTime;
+ this._callCount = this._callInfo.callCount;
+ }
+
+ for (var i = 0; i < this._childNodes.length; ++i)
+ this._childNodes[i].establishRelationships(this, this._childNodes[i - 1], this._childNodes[i + 1]);
+
+ if (this._calls) {
+ for (var i = 0; i < this._calls.length; ++i)
+ this._calls[i].establishRelationships(this, this._calls[i - 1], this._calls[i + 1]);
+ }
+ }
+
+ // Public
+
+ get id()
+ {
+ return this._id;
+ }
+
+ get type()
+ {
+ return this._type;
+ }
+
+ get functionName()
+ {
+ return this._functionName;
+ }
+
+ get sourceCodeLocation()
+ {
+ return this._sourceCodeLocation;
+ }
+
+ get startTime()
+ {
+ if (this._startTime === undefined)
+ this._startTime = Math.max(0, this._calls[0].startTime);
+ return this._startTime;
+ }
+
+ get endTime()
+ {
+ if (this._endTime === undefined)
+ this._endTime = Math.min(this._calls.lastValue.endTime, Infinity);
+ return this._endTime;
+ }
+
+ get selfTime()
+ {
+ this._computeTotalTimesIfNeeded();
+ return this._selfTime;
+ }
+
+ get totalTime()
+ {
+ this._computeTotalTimesIfNeeded();
+ return this._totalTime;
+ }
+
+ get callInfo()
+ {
+ return this._callInfo;
+ }
+
+ get calls()
+ {
+ return this._calls;
+ }
+
+ get previousSibling()
+ {
+ return this._previousSibling;
+ }
+
+ get nextSibling()
+ {
+ return this._nextSibling;
+ }
+
+ get parentNode()
+ {
+ return this._parentNode;
+ }
+
+ get childNodes()
+ {
+ return this._childNodes;
+ }
+
+ computeCallInfoForTimeRange(rangeStartTime, rangeEndTime)
+ {
+ console.assert(typeof rangeStartTime === "number");
+ console.assert(typeof rangeEndTime === "number");
+
+ // With aggregate call info we can't accurately partition self/total/average time
+ // in partial ranges because we don't know exactly when each call started. So we
+ // always return the entire range.
+ if (this._callInfo) {
+ if (this._selfTime === undefined) {
+ var childNodesTotalTime = 0;
+ for (var childNode of this._childNodes)
+ childNodesTotalTime += childNode.totalTime;
+ this._selfTime = this._totalTime - childNodesTotalTime;
+ // selfTime can negative or very close to zero due to floating point error.
+ // Since we show at most four decimal places, treat anything less as zero.
+ if (this._selfTime < 0.0001)
+ this._selfTime = 0.0;
+ }
+
+ return {
+ callCount: this._callCount,
+ startTime: this._startTime,
+ endTime: this._endTime,
+ selfTime: this._selfTime,
+ totalTime: this._totalTime,
+ averageTime: (this._selfTime / this._callCount),
+ };
+ }
+
+ // COMPATIBILITY (iOS 8): Profiles included per-call information and can be finely partitioned.
+ // Compute that below by iterating over all the calls / children for the time range.
+
+ var recordCallCount = true;
+ var callCount = 0;
+
+ function totalTimeInRange(previousValue, call)
+ {
+ if (rangeStartTime > call.endTime || rangeEndTime < call.startTime)
+ return previousValue;
+
+ if (recordCallCount)
+ ++callCount;
+
+ return previousValue + Math.min(call.endTime, rangeEndTime) - Math.max(rangeStartTime, call.startTime);
+ }
+
+ var startTime = Math.max(rangeStartTime, this._calls[0].startTime);
+ var endTime = Math.min(this._calls.lastValue.endTime, rangeEndTime);
+ var totalTime = this._calls.reduce(totalTimeInRange, 0);
+
+ recordCallCount = false;
+
+ var childNodesTotalTime = 0;
+ for (var childNode of this._childNodes)
+ childNodesTotalTime += childNode.calls.reduce(totalTimeInRange, 0);
+
+ var selfTime = totalTime - childNodesTotalTime;
+ var averageTime = selfTime / callCount;
+
+ return {startTime, endTime, totalTime, selfTime, callCount, averageTime};
+ }
+
+ traverseNextProfileNode(stayWithin)
+ {
+ var profileNode = this._childNodes[0];
+ if (profileNode)
+ return profileNode;
+
+ if (this === stayWithin)
+ return null;
+
+ profileNode = this._nextSibling;
+ if (profileNode)
+ return profileNode;
+
+ profileNode = this;
+ while (profileNode && !profileNode.nextSibling && profileNode.parentNode !== stayWithin)
+ profileNode = profileNode.parentNode;
+
+ if (!profileNode)
+ return null;
+
+ return profileNode.nextSibling;
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.ProfileNode.TypeCookieKey] = this._type || null;
+ cookie[WebInspector.ProfileNode.FunctionNameCookieKey] = this._functionName || null;
+ cookie[WebInspector.ProfileNode.SourceCodeURLCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.sourceCode.url ? this._sourceCodeLocation.sourceCode.url.hash : null : null;
+ cookie[WebInspector.ProfileNode.SourceCodeLocationLineCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.lineNumber : null;
+ cookie[WebInspector.ProfileNode.SourceCodeLocationColumnCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.columnNumber : null;
+ }
+
+ // Protected
+
+ establishRelationships(parentNode, previousSibling, nextSibling)
+ {
+ this._parentNode = parentNode || null;
+ this._previousSibling = previousSibling || null;
+ this._nextSibling = nextSibling || null;
+ }
+
+ // Private
+
+ _computeTotalTimesIfNeeded()
+ {
+ if (this._computedTotalTimes)
+ return;
+
+ this._computedTotalTimes = true;
+
+ var info = this.computeCallInfoForTimeRange(0, Infinity);
+ this._startTime = info.startTime;
+ this._endTime = info.endTime;
+ this._selfTime = info.selfTime;
+ this._totalTime = info.totalTime;
+ }
+};
+
+WebInspector.ProfileNode.Type = {
+ Function: "profile-node-type-function",
+ Program: "profile-node-type-program"
+};
+
+WebInspector.ProfileNode.TypeIdentifier = "profile-node";
+WebInspector.ProfileNode.TypeCookieKey = "profile-node-type";
+WebInspector.ProfileNode.FunctionNameCookieKey = "profile-node-function-name";
+WebInspector.ProfileNode.SourceCodeURLCookieKey = "profile-node-source-code-url";
+WebInspector.ProfileNode.SourceCodeLocationLineCookieKey = "profile-node-source-code-location-line";
+WebInspector.ProfileNode.SourceCodeLocationColumnCookieKey = "profile-node-source-code-location-column";
diff --git a/Source/WebInspectorUI/UserInterface/Models/ProfileNodeCall.js b/Source/WebInspectorUI/UserInterface/Models/ProfileNodeCall.js
new file mode 100644
index 000000000..7f74c6598
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ProfileNodeCall.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ProfileNodeCall = class ProfileNodeCall extends WebInspector.Object
+{
+ constructor(startTime, totalTime)
+ {
+ super();
+
+ console.assert(startTime);
+
+ this._startTime = startTime;
+ this._totalTime = totalTime || 0;
+ this._parentNode = null;
+ this._previousSibling = null;
+ this._nextSibling = null;
+ }
+
+ // Public
+
+ get startTime()
+ {
+ return this._startTime;
+ }
+
+ get totalTime()
+ {
+ return this._totalTime;
+ }
+
+ get endTime()
+ {
+ return this._startTime + this._totalTime;
+ }
+
+ // Protected
+
+ establishRelationships(parentNode, previousSibling, nextSibling)
+ {
+ this._parentNode = parentNode || null;
+ this._previousSibling = previousSibling || null;
+ this._nextSibling = nextSibling || null;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/PropertyDescriptor.js b/Source/WebInspectorUI/UserInterface/Models/PropertyDescriptor.js
new file mode 100644
index 000000000..28de2d3f2
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/PropertyDescriptor.js
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.PropertyDescriptor = class PropertyDescriptor extends WebInspector.Object
+{
+ constructor(descriptor, symbol, isOwnProperty, wasThrown, nativeGetter, isInternalProperty)
+ {
+ super();
+
+ console.assert(descriptor);
+ console.assert(descriptor.name);
+ console.assert(!descriptor.value || descriptor.value instanceof WebInspector.RemoteObject);
+ console.assert(!descriptor.get || descriptor.get instanceof WebInspector.RemoteObject);
+ console.assert(!descriptor.set || descriptor.set instanceof WebInspector.RemoteObject);
+ console.assert(!symbol || symbol instanceof WebInspector.RemoteObject);
+
+ this._name = descriptor.name;
+ this._value = descriptor.value;
+ this._hasValue = "value" in descriptor;
+ this._get = descriptor.get;
+ this._set = descriptor.set;
+ this._symbol = symbol;
+
+ this._writable = descriptor.writable || false;
+ this._configurable = descriptor.configurable || false;
+ this._enumerable = descriptor.enumerable || false;
+
+ this._own = isOwnProperty || false;
+ this._wasThrown = wasThrown || false;
+ this._nativeGetterValue = nativeGetter || false;
+ this._internal = isInternalProperty || false;
+ }
+
+ // Static
+
+ // Runtime.PropertyDescriptor or Runtime.InternalPropertyDescriptor (second argument).
+ static fromPayload(payload, internal, target)
+ {
+ if (payload.value)
+ payload.value = WebInspector.RemoteObject.fromPayload(payload.value, target);
+ if (payload.get)
+ payload.get = WebInspector.RemoteObject.fromPayload(payload.get, target);
+ if (payload.set)
+ payload.set = WebInspector.RemoteObject.fromPayload(payload.set, target);
+
+ if (payload.symbol)
+ payload.symbol = WebInspector.RemoteObject.fromPayload(payload.symbol, target);
+
+ if (internal) {
+ console.assert(payload.value);
+ payload.writable = payload.configurable = payload.enumerable = false;
+ payload.isOwn = true;
+ }
+
+ return new WebInspector.PropertyDescriptor(payload, payload.symbol, payload.isOwn, payload.wasThrown, payload.nativeGetter, internal);
+ }
+
+ // Public
+
+ get name() { return this._name; }
+ get value() { return this._value; }
+ get get() { return this._get; }
+ get set() { return this._set; }
+ get writable() { return this._writable; }
+ get configurable() { return this._configurable; }
+ get enumerable() { return this._enumerable; }
+ get symbol() { return this._symbol; }
+ get isOwnProperty() { return this._own; }
+ get wasThrown() { return this._wasThrown; }
+ get nativeGetter() { return this._nativeGetterValue; }
+ get isInternalProperty() { return this._internal; }
+
+ hasValue()
+ {
+ return this._hasValue;
+ }
+
+ hasGetter()
+ {
+ return this._get && this._get.type === "function";
+ }
+
+ hasSetter()
+ {
+ return this._set && this._set.type === "function";
+ }
+
+ isIndexProperty()
+ {
+ return !isNaN(Number(this._name));
+ }
+
+ isSymbolProperty()
+ {
+ return !!this._symbol;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/PropertyPath.js b/Source/WebInspectorUI/UserInterface/Models/PropertyPath.js
new file mode 100644
index 000000000..a2d9c72fe
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/PropertyPath.js
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.PropertyPath = class PropertyPath extends WebInspector.Object
+{
+ constructor(object, pathComponent, parent, isPrototype)
+ {
+ super();
+
+ console.assert(object instanceof WebInspector.RemoteObject || object === null);
+ console.assert(!pathComponent || typeof pathComponent === "string");
+ console.assert(!parent || parent instanceof WebInspector.PropertyPath);
+ console.assert(!parent || pathComponent.length);
+
+ // We allow property pathes with null objects as end-caps only.
+ // Disallow appending to a PropertyPath with null objects.
+ if (parent && !parent.object)
+ throw new Error("Attempted to append to a PropertyPath with null object.");
+
+ this._object = object;
+ this._pathComponent = typeof pathComponent === "string" ? pathComponent : null;
+ this._parent = parent || null;
+ this._isPrototype = isPrototype || false;
+ }
+
+ // Static
+
+ static emptyPropertyPathForScope(object)
+ {
+ return new WebInspector.PropertyPath(object, WebInspector.PropertyPath.SpecialPathComponent.EmptyPathComponentForScope);
+ }
+
+ // Public
+
+ get object() { return this._object; }
+ get parent() { return this._parent; }
+ get isPrototype() { return this._isPrototype; }
+ get pathComponent() { return this._pathComponent; }
+
+ get rootObject()
+ {
+ return this._parent ? this._parent.rootObject : this._object;
+ }
+
+ get lastNonPrototypeObject()
+ {
+ if (!this._parent)
+ return this._object;
+
+ var p = this._parent;
+ while (p) {
+ if (!p.isPrototype)
+ break;
+ if (!p.parent)
+ break;
+ p = p.parent;
+ }
+
+ return p.object;
+ }
+
+ get fullPath()
+ {
+ var components = [];
+ for (var p = this; p && p.pathComponent; p = p.parent)
+ components.push(p.pathComponent);
+ components.reverse();
+ return components.join("");
+ }
+
+ get reducedPath()
+ {
+ // The display path for a value should not include __proto__.
+ // The path for "foo.__proto__.bar.__proto__.x" is better shown as "foo.bar.x".
+ // FIXME: We should keep __proto__ if this property was overridden.
+ var components = [];
+
+ var p = this;
+
+ // Include trailing __proto__s.
+ for (; p && p.isPrototype; p = p.parent)
+ components.push(p.pathComponent);
+
+ // Skip other __proto__s.
+ for (; p && p.pathComponent; p = p.parent) {
+ if (p.isPrototype)
+ continue;
+ components.push(p.pathComponent);
+ }
+
+ components.reverse();
+ return components.join("");
+ }
+
+ displayPath(type)
+ {
+ return type === WebInspector.PropertyPath.Type.Value ? this.reducedPath : this.fullPath;
+ }
+
+ isRoot()
+ {
+ return !this._parent;
+ }
+
+ isScope()
+ {
+ return this._pathComponent === WebInspector.PropertyPath.SpecialPathComponent.EmptyPathComponentForScope;
+ }
+
+ isPathComponentImpossible()
+ {
+ return this._pathComponent && this._pathComponent.startsWith("@");
+ }
+
+ isFullPathImpossible()
+ {
+ if (this.isPathComponentImpossible())
+ return true;
+
+ if (this._parent)
+ return this._parent.isFullPathImpossible();
+
+ return false;
+ }
+
+ appendPropertyName(object, propertyName)
+ {
+ var isPrototype = propertyName === "__proto__";
+
+ if (this.isScope())
+ return new WebInspector.PropertyPath(object, propertyName, this, isPrototype);
+
+ var component = this._canPropertyNameBeDotAccess(propertyName) ? "." + propertyName : "[" + doubleQuotedString(propertyName) + "]";
+ return new WebInspector.PropertyPath(object, component, this, isPrototype);
+ }
+
+ appendPropertySymbol(object, symbolName)
+ {
+ var component = WebInspector.PropertyPath.SpecialPathComponent.SymbolPropertyName + (symbolName.length ? "(" + symbolName + ")" : "");
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendInternalPropertyName(object, propertyName)
+ {
+ var component = WebInspector.PropertyPath.SpecialPathComponent.InternalPropertyName + "[" + propertyName + "]";
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendGetterPropertyName(object, propertyName)
+ {
+ var component = ".__lookupGetter__(" + doubleQuotedString(propertyName) + ")";
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendSetterPropertyName(object, propertyName)
+ {
+ var component = ".__lookupSetter__(" + doubleQuotedString(propertyName) + ")";
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendArrayIndex(object, indexString)
+ {
+ var component = "[" + indexString + "]";
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendMapKey(object)
+ {
+ var component = WebInspector.PropertyPath.SpecialPathComponent.MapKey;
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendMapValue(object, keyObject)
+ {
+ console.assert(!keyObject || keyObject instanceof WebInspector.RemoteObject);
+
+ if (keyObject && keyObject.hasValue()) {
+ if (keyObject.type === "string") {
+ var component = ".get(" + doubleQuotedString(keyObject.description) + ")";
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ var component = ".get(" + keyObject.description + ")";
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ var component = WebInspector.PropertyPath.SpecialPathComponent.MapValue;
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendSetIndex(object)
+ {
+ var component = WebInspector.PropertyPath.SpecialPathComponent.SetIndex;
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendSymbolProperty(object)
+ {
+ var component = WebInspector.PropertyPath.SpecialPathComponent.SymbolPropertyName;
+ return new WebInspector.PropertyPath(object, component, this);
+ }
+
+ appendPropertyDescriptor(object, descriptor, type)
+ {
+ console.assert(descriptor instanceof WebInspector.PropertyDescriptor);
+
+ if (descriptor.isInternalProperty)
+ return this.appendInternalPropertyName(object, descriptor.name);
+ if (descriptor.symbol)
+ return this.appendSymbolProperty(object);
+
+ if (type === WebInspector.PropertyPath.Type.Getter)
+ return this.appendGetterPropertyName(object, descriptor.name);
+ if (type === WebInspector.PropertyPath.Type.Setter)
+ return this.appendSetterPropertyName(object, descriptor.name);
+
+ console.assert(type === WebInspector.PropertyPath.Type.Value);
+
+ if (this._object.subtype === "array" && !isNaN(parseInt(descriptor.name)))
+ return this.appendArrayIndex(object, descriptor.name);
+
+ return this.appendPropertyName(object, descriptor.name);
+ }
+
+ // Private
+
+ _canPropertyNameBeDotAccess(propertyName)
+ {
+ return /^(?![0-9])\w+$/.test(propertyName);
+ }
+};
+
+WebInspector.PropertyPath.SpecialPathComponent = {
+ InternalPropertyName: "@internal",
+ SymbolPropertyName: "@symbol",
+ MapKey: "@mapkey",
+ MapValue: "@mapvalue",
+ SetIndex: "@setindex",
+ EmptyPathComponentForScope: "",
+};
+
+WebInspector.PropertyPath.Type = {
+ Value: "value",
+ Getter: "getter",
+ Setter: "setter",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/PropertyPreview.js b/Source/WebInspectorUI/UserInterface/Models/PropertyPreview.js
new file mode 100644
index 000000000..8ff4dfcb6
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/PropertyPreview.js
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.PropertyPreview = class PropertyPreview extends WebInspector.Object
+{
+ constructor(name, type, subtype, value, valuePreview, isInternalProperty)
+ {
+ super();
+
+ console.assert(typeof name === "string");
+ console.assert(type);
+ console.assert(!value || typeof value === "string");
+ console.assert(!valuePreview || valuePreview instanceof WebInspector.ObjectPreview);
+
+ this._name = name;
+ this._type = type;
+ this._subtype = subtype;
+ this._value = value;
+ this._valuePreview = valuePreview;
+ this._internal = isInternalProperty;
+ }
+
+ // Static
+
+ // Runtime.PropertyPreview.
+ static fromPayload(payload)
+ {
+ if (payload.valuePreview)
+ payload.valuePreview = WebInspector.ObjectPreview.fromPayload(payload.valuePreview);
+
+ return new WebInspector.PropertyPreview(payload.name, payload.type, payload.subtype, payload.value, payload.valuePreview, payload.internal);
+ }
+
+ // Public
+
+ get name() { return this._name; }
+ get type() { return this._type; }
+ get subtype() { return this._subtype; }
+ get value() { return this._value; }
+ get valuePreview() { return this._valuePreview; }
+ get internal() { return this._internal; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/RenderingFrameTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/RenderingFrameTimelineRecord.js
new file mode 100644
index 000000000..d6b502fe0
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/RenderingFrameTimelineRecord.js
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.RenderingFrameTimelineRecord = class RenderingFrameTimelineRecord extends WebInspector.TimelineRecord
+{
+ constructor(startTime, endTime)
+ {
+ super(WebInspector.TimelineRecord.Type.RenderingFrame, startTime, endTime);
+
+ this._durationByTaskType = new Map;
+ this._frameIndex = -1;
+ }
+
+ // Static
+
+ static resetFrameIndex()
+ {
+ WebInspector.RenderingFrameTimelineRecord._nextFrameIndex = 0;
+ }
+
+ static displayNameForTaskType(taskType)
+ {
+ switch (taskType) {
+ case WebInspector.RenderingFrameTimelineRecord.TaskType.Script:
+ return WebInspector.UIString("Script");
+ case WebInspector.RenderingFrameTimelineRecord.TaskType.Layout:
+ return WebInspector.UIString("Layout");
+ case WebInspector.RenderingFrameTimelineRecord.TaskType.Paint:
+ return WebInspector.UIString("Paint");
+ case WebInspector.RenderingFrameTimelineRecord.TaskType.Other:
+ return WebInspector.UIString("Other");
+ }
+ }
+
+ static taskTypeForTimelineRecord(record)
+ {
+ switch (record.type) {
+ case WebInspector.TimelineRecord.Type.Script:
+ return WebInspector.RenderingFrameTimelineRecord.TaskType.Script;
+ case WebInspector.TimelineRecord.Type.Layout:
+ if (record.eventType === WebInspector.LayoutTimelineRecord.EventType.Paint || record.eventType === WebInspector.LayoutTimelineRecord.EventType.Composite)
+ return WebInspector.RenderingFrameTimelineRecord.TaskType.Paint;
+ return WebInspector.RenderingFrameTimelineRecord.TaskType.Layout;
+ default:
+ console.error("Unsupported timeline record type: " + record.type);
+ return null;
+ }
+ }
+
+ // Public
+
+ get frameIndex()
+ {
+ return this._frameIndex;
+ }
+
+ get frameNumber()
+ {
+ return this._frameIndex + 1;
+ }
+
+ setupFrameIndex()
+ {
+ console.assert(this._frameIndex === -1, "Frame index should only be set once.");
+ if (this._frameIndex >= 0)
+ return;
+ this._frameIndex = WebInspector.RenderingFrameTimelineRecord._nextFrameIndex++;
+ }
+
+ durationForTask(taskType)
+ {
+ if (this._durationByTaskType.has(taskType))
+ return this._durationByTaskType.get(taskType);
+
+ var duration;
+ if (taskType === WebInspector.RenderingFrameTimelineRecord.TaskType.Other)
+ duration = this._calculateDurationRemainder();
+ else {
+ duration = this.children.reduce(function(previousValue, currentValue) {
+ if (taskType !== WebInspector.RenderingFrameTimelineRecord.taskTypeForTimelineRecord(currentValue))
+ return previousValue;
+
+ var currentDuration = currentValue.duration;
+ if (currentValue.usesActiveStartTime)
+ currentDuration -= currentValue.inactiveDuration;
+ return previousValue + currentDuration;
+ }, 0);
+
+ if (taskType === WebInspector.RenderingFrameTimelineRecord.TaskType.Script) {
+ // Layout events synchronously triggered from JavaScript must be subtracted from the total
+ // script time, to prevent the time from being counted twice.
+ duration -= this.children.reduce(function(previousValue, currentValue) {
+ if (currentValue.type === WebInspector.TimelineRecord.Type.Layout && (currentValue.sourceCodeLocation || currentValue.callFrames))
+ return previousValue + currentValue.duration;
+ return previousValue;
+ }, 0);
+ }
+ }
+
+ this._durationByTaskType.set(taskType, duration);
+ return duration;
+ }
+
+ // Private
+
+ _calculateDurationRemainder()
+ {
+ return Object.keys(WebInspector.RenderingFrameTimelineRecord.TaskType).reduce((previousValue, key) => {
+ let taskType = WebInspector.RenderingFrameTimelineRecord.TaskType[key];
+ if (taskType === WebInspector.RenderingFrameTimelineRecord.TaskType.Other)
+ return previousValue;
+ return previousValue - this.durationForTask(taskType);
+ }, this.duration);
+ }
+};
+
+WebInspector.RenderingFrameTimelineRecord.TaskType = {
+ Script: "rendering-frame-timeline-record-script",
+ Layout: "rendering-frame-timeline-record-layout",
+ Paint: "rendering-frame-timeline-record-paint",
+ Other: "rendering-frame-timeline-record-other"
+};
+
+WebInspector.RenderingFrameTimelineRecord.TypeIdentifier = "rendering-frame-timeline-record";
+
+WebInspector.RenderingFrameTimelineRecord._nextFrameIndex = 0;
diff --git a/Source/WebInspectorUI/UserInterface/Models/ReplayDashboard.js b/Source/WebInspectorUI/UserInterface/Models/ReplayDashboard.js
new file mode 100644
index 000000000..64629df32
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ReplayDashboard.js
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 University of Washington. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ReplayDashboard = class ReplayDashboard extends WebInspector.Object
+{
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ReplaySession.js b/Source/WebInspectorUI/UserInterface/Models/ReplaySession.js
new file mode 100644
index 000000000..e1966cd88
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ReplaySession.js
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+WebInspector.ReplaySession = class ReplaySession extends WebInspector.Object
+{
+ constructor(identifier)
+ {
+ super();
+
+ this.identifier = identifier;
+ this._segments = [];
+ this._timestamp = null;
+ }
+
+ // Static
+
+ static fromPayload(identifier, payload)
+ {
+ var session = new WebInspector.ReplaySession(identifier);
+ session._updateFromPayload(payload);
+ return session;
+ }
+
+ // Public
+
+ get segments()
+ {
+ return this._segments.slice();
+ }
+
+ segmentsChanged()
+ {
+ // The replay manager won't update the session's list of segments nor create a new session.
+ ReplayAgent.getSessionData(this.identifier)
+ .then(this._updateFromPayload.bind(this));
+ }
+
+ // Private
+
+ _updateFromPayload(payload)
+ {
+ var session = payload.session;
+ console.assert(session.id === this.identifier);
+
+ var segmentIds = session.segments;
+ var oldSegments = this._segments;
+ var pendingSegments = [];
+ for (var segmentId of segmentIds)
+ pendingSegments.push(WebInspector.replayManager.getSegment(segmentId));
+
+ var session = this;
+ Promise.all(pendingSegments).then(
+ function(segmentsArray) {
+ session._segments = segmentsArray;
+ session.dispatchEventToListeners(WebInspector.ReplaySession.Event.SegmentsChanged, {oldSegments});
+ },
+ function(error) {
+ console.error("Problem resolving segments: ", error);
+ }
+ );
+ }
+};
+
+WebInspector.ReplaySession.Event = {
+ SegmentsChanged: "replay-session-segments-changed",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js b/Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js
new file mode 100644
index 000000000..569020eda
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+WebInspector.IncompleteSessionSegment = class IncompleteSessionSegment extends WebInspector.Object
+{
+ constructor(identifier)
+ {
+ super();
+
+ this.identifier = identifier;
+ this._timestamp = Date.now();
+ }
+
+ // Public
+
+ get isComplete()
+ {
+ return false;
+ }
+};
+
+WebInspector.ReplaySessionSegment = class ReplaySessionSegment extends WebInspector.Object
+{
+ constructor(identifier, payload)
+ {
+ super();
+
+ var segment = payload.segment;
+ console.assert(identifier === segment.id);
+
+ this.identifier = identifier;
+ this._timestamp = segment.timestamp;
+
+ this._queues = segment.queues;
+ }
+
+ // Public
+
+ get isComplete()
+ {
+ return true;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Resource.js b/Source/WebInspectorUI/UserInterface/Models/Resource.js
new file mode 100644
index 000000000..29e5a7d6a
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Resource.js
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Resource = class Resource extends WebInspector.SourceCode
+{
+ constructor(url, mimeType, type, loaderIdentifier, targetId, requestIdentifier, requestMethod, requestHeaders, requestData, requestSentTimestamp, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp)
+ {
+ super();
+
+ console.assert(url);
+
+ if (type in WebInspector.Resource.Type)
+ type = WebInspector.Resource.Type[type];
+
+ this._url = url;
+ this._urlComponents = null;
+ this._mimeType = mimeType;
+ this._mimeTypeComponents = null;
+ this._type = type || WebInspector.Resource.typeFromMIMEType(mimeType);
+ this._loaderIdentifier = loaderIdentifier || null;
+ this._requestIdentifier = requestIdentifier || null;
+ this._requestMethod = requestMethod || null;
+ this._requestData = requestData || null;
+ this._requestHeaders = requestHeaders || {};
+ this._responseHeaders = {};
+ this._parentFrame = null;
+ this._initiatorSourceCodeLocation = initiatorSourceCodeLocation || null;
+ this._initiatedResources = [];
+ this._originalRequestWillBeSentTimestamp = originalRequestWillBeSentTimestamp || null;
+ this._requestSentTimestamp = requestSentTimestamp || NaN;
+ this._responseReceivedTimestamp = NaN;
+ this._lastRedirectReceivedTimestamp = NaN;
+ this._lastDataReceivedTimestamp = NaN;
+ this._finishedOrFailedTimestamp = NaN;
+ this._finishThenRequestContentPromise = null;
+ this._size = NaN;
+ this._transferSize = NaN;
+ this._cached = false;
+ this._timingData = new WebInspector.ResourceTimingData(this);
+ this._target = targetId ? WebInspector.targetManager.targetForIdentifier(targetId) : WebInspector.mainTarget;
+
+ if (this._initiatorSourceCodeLocation && this._initiatorSourceCodeLocation.sourceCode instanceof WebInspector.Resource)
+ this._initiatorSourceCodeLocation.sourceCode.addInitiatedResource(this);
+ }
+
+ // Static
+
+ static typeFromMIMEType(mimeType)
+ {
+ if (!mimeType)
+ return WebInspector.Resource.Type.Other;
+
+ mimeType = parseMIMEType(mimeType).type;
+
+ if (mimeType in WebInspector.Resource._mimeTypeMap)
+ return WebInspector.Resource._mimeTypeMap[mimeType];
+
+ if (mimeType.startsWith("image/"))
+ return WebInspector.Resource.Type.Image;
+
+ if (mimeType.startsWith("font/"))
+ return WebInspector.Resource.Type.Font;
+
+ return WebInspector.Resource.Type.Other;
+ }
+
+ static displayNameForType(type, plural)
+ {
+ switch (type) {
+ case WebInspector.Resource.Type.Document:
+ if (plural)
+ return WebInspector.UIString("Documents");
+ return WebInspector.UIString("Document");
+ case WebInspector.Resource.Type.Stylesheet:
+ if (plural)
+ return WebInspector.UIString("Stylesheets");
+ return WebInspector.UIString("Stylesheet");
+ case WebInspector.Resource.Type.Image:
+ if (plural)
+ return WebInspector.UIString("Images");
+ return WebInspector.UIString("Image");
+ case WebInspector.Resource.Type.Font:
+ if (plural)
+ return WebInspector.UIString("Fonts");
+ return WebInspector.UIString("Font");
+ case WebInspector.Resource.Type.Script:
+ if (plural)
+ return WebInspector.UIString("Scripts");
+ return WebInspector.UIString("Script");
+ case WebInspector.Resource.Type.XHR:
+ if (plural)
+ return WebInspector.UIString("XHRs");
+ return WebInspector.UIString("XHR");
+ case WebInspector.Resource.Type.Fetch:
+ if (plural)
+ return WebInspector.UIString("Fetches");
+ return WebInspector.UIString("Fetch");
+ case WebInspector.Resource.Type.WebSocket:
+ if (plural)
+ return WebInspector.UIString("Sockets");
+ return WebInspector.UIString("Socket");
+ case WebInspector.Resource.Type.Other:
+ return WebInspector.UIString("Other");
+ default:
+ console.error("Unknown resource type: ", type);
+ return null;
+ }
+ }
+
+ // Public
+
+ get target() { return this._target; }
+ get type() { return this._type; }
+ get timingData() { return this._timingData; }
+
+ get url()
+ {
+ return this._url;
+ }
+
+ get urlComponents()
+ {
+ if (!this._urlComponents)
+ this._urlComponents = parseURL(this._url);
+ return this._urlComponents;
+ }
+
+ get displayName()
+ {
+ return WebInspector.displayNameForURL(this._url, this.urlComponents);
+ }
+
+ get displayURL()
+ {
+ const isMultiLine = true;
+ const dataURIMaxSize = 64;
+ return WebInspector.truncateURL(this._url, isMultiLine, dataURIMaxSize);
+ }
+
+ get initiatorSourceCodeLocation()
+ {
+ return this._initiatorSourceCodeLocation;
+ }
+
+ get initiatedResources()
+ {
+ return this._initiatedResources;
+ }
+
+ get originalRequestWillBeSentTimestamp()
+ {
+ return this._originalRequestWillBeSentTimestamp;
+ }
+
+ get mimeType()
+ {
+ return this._mimeType;
+ }
+
+ get mimeTypeComponents()
+ {
+ if (!this._mimeTypeComponents)
+ this._mimeTypeComponents = parseMIMEType(this._mimeType);
+ return this._mimeTypeComponents;
+ }
+
+ get syntheticMIMEType()
+ {
+ // Resources are often transferred with a MIME-type that doesn't match the purpose the
+ // resource was loaded for, which is what WebInspector.Resource.Type represents.
+ // This getter generates a MIME-type, if needed, that matches the resource type.
+
+ // If the type matches the Resource.Type of the MIME-type, then return the actual MIME-type.
+ if (this._type === WebInspector.Resource.typeFromMIMEType(this._mimeType))
+ return this._mimeType;
+
+ // Return the default MIME-types for the Resource.Type, since the current MIME-type
+ // does not match what is expected for the Resource.Type.
+ switch (this._type) {
+ case WebInspector.Resource.Type.Document:
+ return "text/html";
+ case WebInspector.Resource.Type.Stylesheet:
+ return "text/css";
+ case WebInspector.Resource.Type.Script:
+ return "text/javascript";
+ }
+
+ // Return the actual MIME-type since we don't have a better synthesized one to return.
+ return this._mimeType;
+ }
+
+ createObjectURL()
+ {
+ // If content is not available, fallback to using original URL.
+ // The client may try to revoke it, but nothing will happen.
+ if (!this.content)
+ return this._url;
+
+ var content = this.content;
+ console.assert(content instanceof Blob, content);
+
+ return URL.createObjectURL(content);
+ }
+
+ isMainResource()
+ {
+ return this._parentFrame ? this._parentFrame.mainResource === this : false;
+ }
+
+ addInitiatedResource(resource)
+ {
+ if (!(resource instanceof WebInspector.Resource))
+ return;
+
+ this._initiatedResources.push(resource);
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.InitiatedResourcesDidChange);
+ }
+
+ get parentFrame()
+ {
+ return this._parentFrame;
+ }
+
+ get loaderIdentifier()
+ {
+ return this._loaderIdentifier;
+ }
+
+ get requestIdentifier()
+ {
+ return this._requestIdentifier;
+ }
+
+ get finished()
+ {
+ return this._finished;
+ }
+
+ get failed()
+ {
+ return this._failed;
+ }
+
+ get canceled()
+ {
+ return this._canceled;
+ }
+
+ get requestMethod()
+ {
+ return this._requestMethod;
+ }
+
+ get requestData()
+ {
+ return this._requestData;
+ }
+
+ get requestDataContentType()
+ {
+ return this._requestHeaders.valueForCaseInsensitiveKey("Content-Type") || null;
+ }
+
+ get requestHeaders()
+ {
+ return this._requestHeaders;
+ }
+
+ get responseHeaders()
+ {
+ return this._responseHeaders;
+ }
+
+ get requestSentTimestamp()
+ {
+ return this._requestSentTimestamp;
+ }
+
+ get lastRedirectReceivedTimestamp()
+ {
+ return this._lastRedirectReceivedTimestamp;
+ }
+
+ get responseReceivedTimestamp()
+ {
+ return this._responseReceivedTimestamp;
+ }
+
+ get lastDataReceivedTimestamp()
+ {
+ return this._lastDataReceivedTimestamp;
+ }
+
+ get finishedOrFailedTimestamp()
+ {
+ return this._finishedOrFailedTimestamp;
+ }
+
+ get firstTimestamp()
+ {
+ return this.timingData.startTime || this.lastRedirectReceivedTimestamp || this.responseReceivedTimestamp || this.lastDataReceivedTimestamp || this.finishedOrFailedTimestamp;
+ }
+
+ get lastTimestamp()
+ {
+ return this.timingData.responseEnd || this.lastDataReceivedTimestamp || this.responseReceivedTimestamp || this.lastRedirectReceivedTimestamp || this.requestSentTimestamp;
+ }
+
+ get duration()
+ {
+ return this.timingData.responseEnd - this.timingData.requestStart;
+ }
+
+ get latency()
+ {
+ return this.timingData.responseStart - this.timingData.requestStart;
+ }
+
+ get receiveDuration()
+ {
+ return this.timingData.responseEnd - this.timingData.responseStart;
+ }
+
+ get cached()
+ {
+ return this._cached;
+ }
+
+ get statusCode()
+ {
+ return this._statusCode;
+ }
+
+ get statusText()
+ {
+ return this._statusText;
+ }
+
+ get size()
+ {
+ return this._size;
+ }
+
+ get encodedSize()
+ {
+ if (!isNaN(this._transferSize))
+ return this._transferSize;
+
+ // If we did not receive actual transfer size from network
+ // stack, we prefer using Content-Length over resourceSize as
+ // resourceSize may differ from actual transfer size if platform's
+ // network stack performed decoding (e.g. gzip decompression).
+ // The Content-Length, though, is expected to come from raw
+ // response headers and will reflect actual transfer length.
+ // This won't work for chunked content encoding, so fall back to
+ // resourceSize when we don't have Content-Length. This still won't
+ // work for chunks with non-trivial encodings. We need a way to
+ // get actual transfer size from the network stack.
+
+ return Number(this._responseHeaders.valueForCaseInsensitiveKey("Content-Length") || this._size);
+ }
+
+ get transferSize()
+ {
+ if (this.statusCode === 304) // Not modified
+ return this._responseHeadersSize;
+
+ if (this._cached)
+ return 0;
+
+ return this._responseHeadersSize + this.encodedSize;
+ }
+
+ get compressed()
+ {
+ var contentEncoding = this._responseHeaders.valueForCaseInsensitiveKey("Content-Encoding");
+ return contentEncoding && /\b(?:gzip|deflate)\b/.test(contentEncoding);
+ }
+
+ get scripts()
+ {
+ return this._scripts || [];
+ }
+
+ scriptForLocation(sourceCodeLocation)
+ {
+ console.assert(!(this instanceof WebInspector.SourceMapResource));
+ console.assert(sourceCodeLocation.sourceCode === this, "SourceCodeLocation must be in this Resource");
+ if (sourceCodeLocation.sourceCode !== this)
+ return null;
+
+ var lineNumber = sourceCodeLocation.lineNumber;
+ var columnNumber = sourceCodeLocation.columnNumber;
+ for (var i = 0; i < this._scripts.length; ++i) {
+ var script = this._scripts[i];
+ if (script.range.startLine <= lineNumber && script.range.endLine >= lineNumber) {
+ if (script.range.startLine === lineNumber && columnNumber < script.range.startColumn)
+ continue;
+ if (script.range.endLine === lineNumber && columnNumber > script.range.endColumn)
+ continue;
+ return script;
+ }
+ }
+
+ return null;
+ }
+
+ updateForRedirectResponse(url, requestHeaders, elapsedTime)
+ {
+ console.assert(!this._finished);
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+
+ var oldURL = this._url;
+
+ this._url = url;
+ this._requestHeaders = requestHeaders || {};
+ this._lastRedirectReceivedTimestamp = elapsedTime || NaN;
+
+ if (oldURL !== url) {
+ // Delete the URL components so the URL is re-parsed the next time it is requested.
+ this._urlComponents = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.URLDidChange, {oldURL});
+ }
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.RequestHeadersDidChange);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ updateForResponse(url, mimeType, type, responseHeaders, statusCode, statusText, elapsedTime, timingData)
+ {
+ console.assert(!this._finished);
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+
+ var oldURL = this._url;
+ var oldMIMEType = this._mimeType;
+ var oldType = this._type;
+
+ if (type in WebInspector.Resource.Type)
+ type = WebInspector.Resource.Type[type];
+
+ this._url = url;
+ this._mimeType = mimeType;
+ this._type = type || WebInspector.Resource.typeFromMIMEType(mimeType);
+ this._statusCode = statusCode;
+ this._statusText = statusText;
+ this._responseHeaders = responseHeaders || {};
+ this._responseReceivedTimestamp = elapsedTime || NaN;
+ this._timingData = WebInspector.ResourceTimingData.fromPayload(timingData, this);
+
+ this._responseHeadersSize = String(this._statusCode).length + this._statusText.length + 12; // Extra length is for "HTTP/1.1 ", " ", and "\r\n".
+ for (var name in this._responseHeaders)
+ this._responseHeadersSize += name.length + this._responseHeaders[name].length + 4; // Extra length is for ": ", and "\r\n".
+
+ if (statusCode === 304 && !this._cached)
+ this.markAsCached();
+
+ if (oldURL !== url) {
+ // Delete the URL components so the URL is re-parsed the next time it is requested.
+ this._urlComponents = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.URLDidChange, {oldURL});
+ }
+
+ if (oldMIMEType !== mimeType) {
+ // Delete the MIME-type components so the MIME-type is re-parsed the next time it is requested.
+ this._mimeTypeComponents = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.MIMETypeDidChange, {oldMIMEType});
+ }
+
+ if (oldType !== type)
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TypeDidChange, {oldType});
+
+ console.assert(isNaN(this._size));
+ console.assert(isNaN(this._transferSize));
+
+ // The transferSize becomes 0 when status is 304 or Content-Length is available, so
+ // notify listeners of that change.
+ if (statusCode === 304 || this._responseHeaders.valueForCaseInsensitiveKey("Content-Length"))
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.ResponseReceived);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ canRequestContent()
+ {
+ return this._finished;
+ }
+
+ requestContentFromBackend()
+ {
+ // If we have the requestIdentifier we can get the actual response for this specific resource.
+ // Otherwise the content will be cached resource data, which might not exist anymore.
+ if (this._requestIdentifier)
+ return NetworkAgent.getResponseBody(this._requestIdentifier);
+
+ // There is no request identifier or frame to request content from.
+ if (this._parentFrame)
+ return PageAgent.getResourceContent(this._parentFrame.id, this._url);
+
+ return Promise.reject(new Error("Content request failed."));
+ }
+
+ increaseSize(dataLength, elapsedTime)
+ {
+ console.assert(dataLength >= 0);
+
+ if (isNaN(this._size))
+ this._size = 0;
+
+ var previousSize = this._size;
+
+ this._size += dataLength;
+
+ this._lastDataReceivedTimestamp = elapsedTime || NaN;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.SizeDidChange, {previousSize});
+
+ // The transferSize is based off of size when status is not 304 or Content-Length is missing.
+ if (isNaN(this._transferSize) && this._statusCode !== 304 && !this._responseHeaders.valueForCaseInsensitiveKey("Content-Length"))
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+ }
+
+ increaseTransferSize(encodedDataLength)
+ {
+ console.assert(encodedDataLength >= 0);
+
+ if (isNaN(this._transferSize))
+ this._transferSize = 0;
+ this._transferSize += encodedDataLength;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+ }
+
+ markAsCached()
+ {
+ this._cached = true;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.CacheStatusDidChange);
+
+ // The transferSize is starts returning 0 when cached is true, unless status is 304.
+ if (this._statusCode !== 304)
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+ }
+
+ markAsFinished(elapsedTime)
+ {
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+
+ this._finished = true;
+ this._finishedOrFailedTimestamp = elapsedTime || NaN;
+ this._timingData.markResponseEndTime(elapsedTime || NaN);
+
+ if (this._finishThenRequestContentPromise)
+ this._finishThenRequestContentPromise = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.LoadingDidFinish);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ markAsFailed(canceled, elapsedTime)
+ {
+ console.assert(!this._finished);
+
+ this._failed = true;
+ this._canceled = canceled;
+ this._finishedOrFailedTimestamp = elapsedTime || NaN;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.LoadingDidFail);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ revertMarkAsFinished()
+ {
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+ console.assert(this._finished);
+
+ this._finished = false;
+ this._finishedOrFailedTimestamp = NaN;
+ }
+
+ getImageSize(callback)
+ {
+ // Throw an error in the case this resource is not an image.
+ if (this.type !== WebInspector.Resource.Type.Image)
+ throw "Resource is not an image.";
+
+ // See if we've already computed and cached the image size,
+ // in which case we can provide them directly.
+ if (this._imageSize) {
+ callback(this._imageSize);
+ return;
+ }
+
+ var objectURL = null;
+
+ // Event handler for the image "load" event.
+ function imageDidLoad()
+ {
+ URL.revokeObjectURL(objectURL);
+
+ // Cache the image metrics.
+ this._imageSize = {
+ width: image.width,
+ height: image.height
+ };
+
+ callback(this._imageSize);
+ }
+
+ // Create an <img> element that we'll use to load the image resource
+ // so that we can query its intrinsic size.
+ var image = new Image;
+ image.addEventListener("load", imageDidLoad.bind(this), false);
+
+ // Set the image source using an object URL once we've obtained its data.
+ this.requestContent().then(function(content) {
+ objectURL = image.src = content.sourceCode.createObjectURL();
+ });
+ }
+
+ requestContent()
+ {
+ if (this._finished)
+ return super.requestContent();
+
+ if (this._failed)
+ return Promise.resolve({error: WebInspector.UIString("An error occurred trying to load the resource.")});
+
+ if (!this._finishThenRequestContentPromise) {
+ this._finishThenRequestContentPromise = new Promise((resolve, reject) => {
+ this.addEventListener(WebInspector.Resource.Event.LoadingDidFinish, resolve);
+ this.addEventListener(WebInspector.Resource.Event.LoadingDidFail, reject);
+ }).then(WebInspector.SourceCode.prototype.requestContent.bind(this));
+ }
+
+ return this._finishThenRequestContentPromise;
+ }
+
+ associateWithScript(script)
+ {
+ if (!this._scripts)
+ this._scripts = [];
+
+ this._scripts.push(script);
+
+ if (this._type === WebInspector.Resource.Type.Other || this._type === WebInspector.Resource.Type.XHR) {
+ let oldType = this._type;
+ this._type = WebInspector.Resource.Type.Script;
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TypeDidChange, {oldType});
+ }
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.Resource.URLCookieKey] = this.url.hash;
+ cookie[WebInspector.Resource.MainResourceCookieKey] = this.isMainResource();
+ }
+
+ generateCURLCommand()
+ {
+ function escapeStringPosix(str) {
+ function escapeCharacter(x) {
+ let code = x.charCodeAt(0);
+ let hex = code.toString(16);
+ if (code < 256)
+ return "\\x" + hex.padStart(2, "0");
+ return "\\u" + hex.padStart(4, "0");
+ }
+
+ if (/[^\x20-\x7E]|'/.test(str)) {
+ // Use ANSI-C quoting syntax.
+ return "$'" + str.replace(/\\/g, "\\\\")
+ .replace(/'/g, "\\'")
+ .replace(/\n/g, "\\n")
+ .replace(/\r/g, "\\r")
+ .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
+ } else {
+ // Use single quote syntax.
+ return `'${str}'`;
+ }
+ }
+
+ let command = ["curl " + escapeStringPosix(this.url).replace(/[[{}\]]/g, "\\$&")];
+ command.push(`-X${this.requestMethod}`);
+
+ for (let key in this.requestHeaders)
+ command.push("-H " + escapeStringPosix(`${key}: ${this.requestHeaders[key]}`));
+
+ if (this.requestDataContentType && this.requestMethod !== "GET" && this.requestData) {
+ if (this.requestDataContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i))
+ command.push("--data " + escapeStringPosix(this.requestData));
+ else
+ command.push("--data-binary " + escapeStringPosix(this.requestData));
+ }
+
+ let curlCommand = command.join(" \\\n");
+ InspectorFrontendHost.copyText(curlCommand);
+ return curlCommand;
+ }
+};
+
+WebInspector.Resource.TypeIdentifier = "resource";
+WebInspector.Resource.URLCookieKey = "resource-url";
+WebInspector.Resource.MainResourceCookieKey = "resource-is-main-resource";
+
+WebInspector.Resource.Event = {
+ URLDidChange: "resource-url-did-change",
+ MIMETypeDidChange: "resource-mime-type-did-change",
+ TypeDidChange: "resource-type-did-change",
+ RequestHeadersDidChange: "resource-request-headers-did-change",
+ ResponseReceived: "resource-response-received",
+ LoadingDidFinish: "resource-loading-did-finish",
+ LoadingDidFail: "resource-loading-did-fail",
+ TimestampsDidChange: "resource-timestamps-did-change",
+ SizeDidChange: "resource-size-did-change",
+ TransferSizeDidChange: "resource-transfer-size-did-change",
+ CacheStatusDidChange: "resource-cached-did-change",
+ InitiatedResourcesDidChange: "resource-initiated-resources-did-change",
+};
+
+// Keep these in sync with the "ResourceType" enum defined by the "Page" domain.
+WebInspector.Resource.Type = {
+ Document: "resource-type-document",
+ Stylesheet: "resource-type-stylesheet",
+ Image: "resource-type-image",
+ Font: "resource-type-font",
+ Script: "resource-type-script",
+ XHR: "resource-type-xhr",
+ Fetch: "resource-type-fetch",
+ WebSocket: "resource-type-websocket",
+ Other: "resource-type-other"
+};
+
+// This MIME Type map is private, use WebInspector.Resource.typeFromMIMEType().
+WebInspector.Resource._mimeTypeMap = {
+ "text/html": WebInspector.Resource.Type.Document,
+ "text/xml": WebInspector.Resource.Type.Document,
+ "text/plain": WebInspector.Resource.Type.Document,
+ "application/xhtml+xml": WebInspector.Resource.Type.Document,
+ "image/svg+xml": WebInspector.Resource.Type.Document,
+
+ "text/css": WebInspector.Resource.Type.Stylesheet,
+ "text/xsl": WebInspector.Resource.Type.Stylesheet,
+ "text/x-less": WebInspector.Resource.Type.Stylesheet,
+ "text/x-sass": WebInspector.Resource.Type.Stylesheet,
+ "text/x-scss": WebInspector.Resource.Type.Stylesheet,
+
+ "application/pdf": WebInspector.Resource.Type.Image,
+
+ "application/x-font-type1": WebInspector.Resource.Type.Font,
+ "application/x-font-ttf": WebInspector.Resource.Type.Font,
+ "application/x-font-woff": WebInspector.Resource.Type.Font,
+ "application/x-truetype-font": WebInspector.Resource.Type.Font,
+
+ "text/javascript": WebInspector.Resource.Type.Script,
+ "text/ecmascript": WebInspector.Resource.Type.Script,
+ "application/javascript": WebInspector.Resource.Type.Script,
+ "application/ecmascript": WebInspector.Resource.Type.Script,
+ "application/x-javascript": WebInspector.Resource.Type.Script,
+ "application/json": WebInspector.Resource.Type.Script,
+ "application/x-json": WebInspector.Resource.Type.Script,
+ "text/x-javascript": WebInspector.Resource.Type.Script,
+ "text/x-json": WebInspector.Resource.Type.Script,
+ "text/javascript1.1": WebInspector.Resource.Type.Script,
+ "text/javascript1.2": WebInspector.Resource.Type.Script,
+ "text/javascript1.3": WebInspector.Resource.Type.Script,
+ "text/jscript": WebInspector.Resource.Type.Script,
+ "text/livescript": WebInspector.Resource.Type.Script,
+ "text/x-livescript": WebInspector.Resource.Type.Script,
+ "text/typescript": WebInspector.Resource.Type.Script,
+ "text/x-clojure": WebInspector.Resource.Type.Script,
+ "text/x-coffeescript": WebInspector.Resource.Type.Script
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ResourceCollection.js b/Source/WebInspectorUI/UserInterface/Models/ResourceCollection.js
new file mode 100644
index 000000000..bd38ad2f5
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ResourceCollection.js
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Devin Rousso <dcrousso+webkit@gmail.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ResourceCollection = class ResourceCollection extends WebInspector.Collection
+{
+ constructor(resourceType)
+ {
+ super(WebInspector.ResourceCollection.verifierForType(resourceType));
+
+ this._resourceType = resourceType || null;
+ this._resourceURLMap = new Map;
+ this._resourcesTypeMap = new Map;
+ }
+
+ // Static
+
+ static verifierForType(type) {
+ switch (type) {
+ case WebInspector.Resource.Type.Document:
+ return WebInspector.ResourceCollection.TypeVerifier.Document;
+ case WebInspector.Resource.Type.Stylesheet:
+ return WebInspector.ResourceCollection.TypeVerifier.Stylesheet;
+ case WebInspector.Resource.Type.Image:
+ return WebInspector.ResourceCollection.TypeVerifier.Image;
+ case WebInspector.Resource.Type.Font:
+ return WebInspector.ResourceCollection.TypeVerifier.Font;
+ case WebInspector.Resource.Type.Script:
+ return WebInspector.ResourceCollection.TypeVerifier.Script;
+ case WebInspector.Resource.Type.XHR:
+ return WebInspector.ResourceCollection.TypeVerifier.XHR;
+ case WebInspector.Resource.Type.Fetch:
+ return WebInspector.ResourceCollection.TypeVerifier.Fetch;
+ case WebInspector.Resource.Type.WebSocket:
+ return WebInspector.ResourceCollection.TypeVerifier.WebSocket;
+ case WebInspector.Resource.Type.Other:
+ return WebInspector.ResourceCollection.TypeVerifier.Other;
+ default:
+ return WebInspector.Collection.TypeVerifier.Resource;
+ }
+ }
+
+ // Public
+
+ resourceForURL(url)
+ {
+ return this._resourceURLMap.get(url) || null;
+ }
+
+ resourceCollectionForType(type)
+ {
+ if (this._resourceType) {
+ console.assert(type === this._resourceType);
+ return this;
+ }
+
+ let resourcesCollectionForType = this._resourcesTypeMap.get(type);
+ if (!resourcesCollectionForType) {
+ resourcesCollectionForType = new WebInspector.ResourceCollection(type);
+ this._resourcesTypeMap.set(type, resourcesCollectionForType);
+ }
+
+ return resourcesCollectionForType;
+ }
+
+ clear()
+ {
+ super.clear();
+
+ this._resourceURLMap.clear();
+
+ if (!this._resourceType)
+ this._resourcesTypeMap.clear();
+ }
+
+ // Protected
+
+ itemAdded(item)
+ {
+ this._associateWithResource(item);
+ }
+
+ itemRemoved(item)
+ {
+ this._disassociateWithResource(item);
+ }
+
+ itemsCleared(items)
+ {
+ const skipRemoval = true;
+
+ for (let item of items)
+ this._disassociateWithResource(item, skipRemoval);
+ }
+
+ // Private
+
+ _associateWithResource(resource)
+ {
+ this._resourceURLMap.set(resource.url, resource);
+
+ if (!this._resourceType) {
+ let resourcesCollectionForType = this.resourceCollectionForType(resource.type);
+ resourcesCollectionForType.add(resource);
+ }
+
+ resource.addEventListener(WebInspector.Resource.Event.URLDidChange, this._resourceURLDidChange, this);
+ resource.addEventListener(WebInspector.Resource.Event.TypeDidChange, this._resourceTypeDidChange, this);
+ }
+
+ _disassociateWithResource(resource, skipRemoval)
+ {
+ resource.removeEventListener(WebInspector.Resource.Event.URLDidChange, this._resourceURLDidChange, this);
+ resource.removeEventListener(WebInspector.Resource.Event.TypeDidChange, this._resourceTypeDidChange, this);
+
+ if (skipRemoval)
+ return;
+
+ if (!this._resourceType) {
+ let resourcesCollectionForType = this.resourceCollectionForType(resource.type);
+ resourcesCollectionForType.remove(resource);
+ }
+
+ this._resourceURLMap.delete(resource.url);
+ }
+
+ _resourceURLDidChange(event)
+ {
+ let resource = event.target;
+ console.assert(resource instanceof WebInspector.Resource);
+ if (!(resource instanceof WebInspector.Resource))
+ return;
+
+ let oldURL = event.data.oldURL;
+ console.assert(oldURL);
+ if (!oldURL)
+ return;
+
+ this._resourceURLMap.set(resource.url, resource);
+ this._resourceURLMap.delete(oldURL);
+ }
+
+ _resourceTypeDidChange(event)
+ {
+ let resource = event.target;
+ console.assert(resource instanceof WebInspector.Resource);
+ if (!(resource instanceof WebInspector.Resource))
+ return;
+
+ if (this._resourceType) {
+ console.assert(resource.type !== this._resourceType);
+ this.remove(resource);
+ return;
+ }
+
+ console.assert(event.data.oldType);
+
+ let resourcesWithNewType = this.resourceCollectionForType(resource.type);
+ resourcesWithNewType.add(resource);
+
+ // It is not necessary to remove the resource from the sub-collection for the old type since
+ // this is handled by that sub-collection's own _resourceTypeDidChange handler (via the
+ // above if statement).
+ }
+};
+
+WebInspector.ResourceCollection.TypeVerifier = {
+ Document: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.Document,
+ Stylesheet: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.Stylesheet,
+ Image: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.Image,
+ Font: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.Font,
+ Script: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.Script,
+ XHR: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.XHR,
+ Fetch: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.Fetch,
+ WebSocket: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.WebSocket,
+ Other: (object) => WebInspector.Collection.TypeVerifier.Resource(object) && object.type === WebInspector.Resource.Type.Other,
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ResourceQueryMatch.js b/Source/WebInspectorUI/UserInterface/Models/ResourceQueryMatch.js
new file mode 100644
index 000000000..0668525bc
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ResourceQueryMatch.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ResourceQueryMatch = class QueryMatch extends WebInspector.Object
+{
+ constructor(type, index, queryIndex)
+ {
+ super();
+
+ this._type = type;
+ this._index = index;
+ this._queryIndex = queryIndex;
+ this._rank = undefined;
+ }
+
+ // Public
+
+ get type() { return this._type; }
+ get index() { return this._index; }
+ get queryIndex() { return this._queryIndex; }
+};
+
+WebInspector.ResourceQueryMatch.Type = {
+ Normal: Symbol("normal"),
+ Special: Symbol("special")
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ResourceQueryResult.js b/Source/WebInspectorUI/UserInterface/Models/ResourceQueryResult.js
new file mode 100644
index 000000000..c2bf09d37
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ResourceQueryResult.js
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ResourceQueryResult = class QueryResult extends WebInspector.Object
+{
+ constructor(resource, matches, cookie)
+ {
+ console.assert(matches.length, "Query matches list can't be empty.");
+
+ super();
+
+ this._resource = resource;
+ this._matches = matches;
+ this._cookie = cookie || null;
+ }
+
+ // Public
+
+ get resource() { return this._resource; }
+ get cookie() { return this._cookie; }
+
+ get rank()
+ {
+ if (this._rank === undefined)
+ this._calculateRank();
+
+ return this._rank;
+ }
+
+ get matchingTextRanges()
+ {
+ if (!this._matchingTextRanges)
+ this._matchingTextRanges = this._createMatchingTextRanges();
+
+ return this._matchingTextRanges;
+ }
+
+ // Private
+
+ _calculateRank()
+ {
+ const normalWeight = 10;
+ const consecutiveWeight = 5;
+ const specialMultiplier = 5;
+
+ function getMultiplier(match) {
+ if (match.type === WebInspector.ResourceQueryMatch.Type.Special)
+ return specialMultiplier;
+
+ return 1;
+ }
+
+ this._rank = 0;
+
+ let previousMatch = null;
+ let consecutiveMatchStart = null;
+ for (let match of this._matches) {
+ this._rank += normalWeight * getMultiplier(match);
+
+ let consecutive = previousMatch && previousMatch.index === match.index - 1;
+ if (consecutive) {
+ if (!consecutiveMatchStart)
+ consecutiveMatchStart = previousMatch;
+
+ // If the first match in this consecutive series was a special character, give a
+ // bonus (more likely to match a specific word in the text). Otherwise, multiply
+ // by the current length of the consecutive sequence (gives priority to fewer
+ // longer sequences instead of more short sequences).
+ this._rank += consecutiveWeight * getMultiplier(consecutiveMatchStart) * (match.index - consecutiveMatchStart.index);
+ } else if (consecutiveMatchStart)
+ consecutiveMatchStart = null;
+
+ previousMatch = match;
+
+ // The match index is deducted from the total rank, so matches that occur closer to
+ // the beginning of the string are ranked higher. Increase the amount subtracted if
+ // the match is special, so as to favor matches towards the beginning of the string.
+ if (!consecutive)
+ this._rank -= match.index * getMultiplier(match);
+ }
+ }
+
+ _createMatchingTextRanges()
+ {
+ if (!this._matches.length)
+ return [];
+
+ let ranges = [];
+ let startIndex = this._matches[0].index;
+ let endIndex = startIndex;
+ for (let i = 1; i < this._matches.length; ++i) {
+ let match = this._matches[i];
+
+ // Increment endIndex for consecutive match.
+ if (match.index === endIndex + 1) {
+ endIndex++;
+ continue;
+ }
+
+ // Begin a new range when a gap between this match and the previous match is found.
+ ranges.push(new WebInspector.TextRange(0, startIndex, 0, endIndex + 1));
+ startIndex = match.index;
+ endIndex = startIndex;
+ }
+
+ ranges.push(new WebInspector.TextRange(0, startIndex, 0, endIndex + 1));
+ return ranges;
+ }
+
+ // Testing
+
+ __test_createMatchesMask()
+ {
+ let filename = this._resource.displayName;
+ let lastIndex = -1;
+ let result = "";
+
+ for (let match of this._matches) {
+ let gap = " ".repeat(match.index - lastIndex - 1);
+ result += gap;
+ result += filename[match.index];
+ lastIndex = match.index;
+ }
+
+ return result;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js
new file mode 100644
index 000000000..03a090971
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ResourceTimelineRecord = class ResourceTimelineRecord extends WebInspector.TimelineRecord
+{
+ constructor(resource)
+ {
+ super(WebInspector.TimelineRecord.Type.Network);
+
+ this._resource = resource;
+ this._resource.addEventListener(WebInspector.Resource.Event.TimestampsDidChange, this._dispatchUpdatedEvent, this);
+ }
+
+ // Public
+
+ get resource()
+ {
+ return this._resource;
+ }
+
+ get updatesDynamically()
+ {
+ return true;
+ }
+
+ get usesActiveStartTime()
+ {
+ return true;
+ }
+
+ get startTime()
+ {
+ return this._resource.timingData.startTime;
+ }
+
+ get activeStartTime()
+ {
+ return this._resource.timingData.responseStart;
+ }
+
+ get endTime()
+ {
+ return this._resource.timingData.responseEnd;
+ }
+
+ // Private
+
+ _dispatchUpdatedEvent()
+ {
+ this.dispatchEventToListeners(WebInspector.TimelineRecord.Event.Updated);
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ResourceTimingData.js b/Source/WebInspectorUI/UserInterface/Models/ResourceTimingData.js
new file mode 100644
index 000000000..2affdf73a
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ResourceTimingData.js
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ResourceTimingData = class ResourceTimingData extends WebInspector.Object
+{
+ constructor(resource, data)
+ {
+ super();
+
+ data = data || {};
+
+ console.assert(isNaN(data.domainLookupStart) === isNaN(data.domainLookupEnd));
+ console.assert(isNaN(data.connectStart) === isNaN(data.connectEnd));
+
+ this._resource = resource;
+
+ this._startTime = data.startTime || NaN;
+ this._domainLookupStart = data.domainLookupStart || NaN;
+ this._domainLookupEnd = data.domainLookupEnd || NaN;
+ this._connectStart = data.connectStart || NaN;
+ this._connectEnd = data.connectEnd || NaN;
+ this._secureConnectionStart = data.secureConnectionStart || NaN;
+ this._requestStart = data.requestStart || NaN;
+ this._responseStart = data.responseStart || NaN;
+ this._responseEnd = data.responseEnd || NaN;
+
+ if (this._domainLookupStart >= this._domainLookupEnd)
+ this._domainLookupStart = this._domainLookupEnd = NaN;
+
+ if (this._connectStart >= this._connectEnd)
+ this._connectStart = this._connectEnd = NaN;
+ }
+
+ // Static
+
+ static fromPayload(payload, resource)
+ {
+ payload = payload || {};
+
+ // COMPATIBILITY (iOS 10): Resource Timing data was incomplete and incorrect. Do not use it.
+ // iOS 7 sent a requestTime and iOS 8-9.3 sent a navigationStart time.
+ if (typeof payload.requestTime === "number" || typeof payload.navigationStart === "number")
+ payload = {};
+
+ function offsetToTimestamp(offset) {
+ return offset > 0 ? payload.startTime + offset / 1000 : NaN;
+ }
+
+ let data = {
+ startTime: payload.startTime,
+ domainLookupStart: offsetToTimestamp(payload.domainLookupStart),
+ domainLookupEnd: offsetToTimestamp(payload.domainLookupEnd),
+ connectStart: offsetToTimestamp(payload.connectStart),
+ connectEnd: offsetToTimestamp(payload.connectEnd),
+ secureConnectionStart: offsetToTimestamp(payload.secureConnectionStart),
+ requestStart: offsetToTimestamp(payload.requestStart),
+ responseStart: offsetToTimestamp(payload.responseStart),
+ responseEnd: offsetToTimestamp(payload.responseEnd)
+ };
+
+ // COMPATIBILITY (iOS 8): connectStart is zero if a secure connection is used.
+ if (isNaN(data.connectStart) && !isNaN(data.secureConnectionStart))
+ data.connectStart = data.secureConnectionStart;
+
+ return new WebInspector.ResourceTimingData(resource, data);
+ }
+
+ // Public
+
+ get startTime() { return this._startTime || this._resource.requestSentTimestamp; }
+ get domainLookupStart() { return this._domainLookupStart; }
+ get domainLookupEnd() { return this._domainLookupEnd; }
+ get connectStart() { return this._connectStart; }
+ get connectEnd() { return this._connectEnd; }
+ get secureConnectionStart() { return this._secureConnectionStart; }
+ get requestStart() { return this._requestStart || this._resource.requestSentTimestamp; }
+ get responseStart() { return this._responseStart || this._resource.responseReceivedTimestamp; }
+ get responseEnd() { return this._responseEnd || this._resource.finishedOrFailedTimestamp; }
+
+ markResponseEndTime(responseEnd)
+ {
+ console.assert(typeof responseEnd === "number");
+ this._responseEnd = responseEnd;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Revision.js b/Source/WebInspectorUI/UserInterface/Models/Revision.js
new file mode 100644
index 000000000..83c51a7a3
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Revision.js
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Revision = class Revision extends WebInspector.Object
+{
+ // Public
+
+ apply()
+ {
+ // Implemented by subclasses.
+ console.error("Needs to be implemented by a subclass.");
+ }
+
+ revert()
+ {
+ // Implemented by subclasses.
+ console.error("Needs to be implemented by a subclass.");
+ }
+
+ copy()
+ {
+ // Override by subclasses.
+ return this;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ScopeChainNode.js b/Source/WebInspectorUI/UserInterface/Models/ScopeChainNode.js
new file mode 100644
index 000000000..8e3811e80
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ScopeChainNode.js
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ScopeChainNode = class ScopeChainNode extends WebInspector.Object
+{
+ constructor(type, objects, name, location, empty)
+ {
+ super();
+
+ console.assert(typeof type === "string");
+ console.assert(objects.every((x) => x instanceof WebInspector.RemoteObject));
+
+ if (type in WebInspector.ScopeChainNode.Type)
+ type = WebInspector.ScopeChainNode.Type[type];
+
+ this._type = type || null;
+ this._objects = objects || [];
+ this._name = name || "";
+ this._location = location || null;
+ this._empty = empty || false;
+ }
+
+ // Public
+
+ get type() { return this._type; }
+ get objects() { return this._objects; }
+ get name() { return this._name; }
+ get location() { return this._location; }
+ get empty() { return this._empty; }
+
+ get hash()
+ {
+ if (this._hash)
+ return this._hash;
+
+ this._hash = this._name;
+ if (this._location)
+ this._hash += `:${this._location.scriptId}:${this._location.lineNumber}:${this._location.columnNumber}`;
+ return this._hash;
+ }
+
+ convertToLocalScope()
+ {
+ this._type = WebInspector.ScopeChainNode.Type.Local;
+ }
+};
+
+WebInspector.ScopeChainNode.Type = {
+ Local: "scope-chain-type-local",
+ Global: "scope-chain-type-global",
+ GlobalLexicalEnvironment: "scope-chain-type-global-lexical-environment",
+ With: "scope-chain-type-with",
+ Closure: "scope-chain-type-closure",
+ Catch: "scope-chain-type-catch",
+ FunctionName: "scope-chain-type-function-name",
+ Block: "scope-chain-type-block",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Script.js b/Source/WebInspectorUI/UserInterface/Models/Script.js
new file mode 100644
index 000000000..22626ce47
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Script.js
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Script = class Script extends WebInspector.SourceCode
+{
+ constructor(target, id, range, url, sourceType, injected, sourceURL, sourceMapURL)
+ {
+ super();
+
+ console.assert(id);
+ console.assert(target instanceof WebInspector.Target);
+ console.assert(range instanceof WebInspector.TextRange);
+
+ this._target = target;
+ this._id = id || null;
+ this._range = range || null;
+ this._url = url || null;
+ this._sourceType = sourceType || WebInspector.Script.SourceType.Program;
+ this._sourceURL = sourceURL || null;
+ this._sourceMappingURL = sourceMapURL || null;
+ this._injected = injected || false;
+ this._dynamicallyAddedScriptElement = false;
+ this._scriptSyntaxTree = null;
+
+ this._resource = this._resolveResource();
+
+ // If this Script was a dynamically added <script> to a Document,
+ // do not associate with the Document resource, instead associate
+ // with the frame as a dynamic script.
+ if (this._resource && this._resource.type === WebInspector.Resource.Type.Document && !this._range.startLine && !this._range.startColumn) {
+ console.assert(this._resource.isMainResource());
+ let documentResource = this._resource;
+ this._resource = null;
+ this._dynamicallyAddedScriptElement = true;
+ documentResource.parentFrame.addExtraScript(this);
+ this._dynamicallyAddedScriptElementNumber = documentResource.parentFrame.extraScriptCollection.items.size;
+ } else if (this._resource)
+ this._resource.associateWithScript(this);
+
+ if (isWebInspectorConsoleEvaluationScript(this._sourceURL)) {
+ // Assign a unique number to the script object so it will stay the same.
+ this._uniqueDisplayNameNumber = this.constructor._nextUniqueConsoleDisplayNameNumber++;
+ }
+
+ if (this._sourceMappingURL)
+ WebInspector.sourceMapManager.downloadSourceMap(this._sourceMappingURL, this._url, this);
+ }
+
+ // Static
+
+ static resetUniqueDisplayNameNumbers()
+ {
+ WebInspector.Script._nextUniqueDisplayNameNumber = 1;
+ WebInspector.Script._nextUniqueConsoleDisplayNameNumber = 1;
+ }
+
+ // Public
+
+ get target() { return this._target; }
+ get id() { return this._id; }
+ get range() { return this._range; }
+ get url() { return this._url; }
+ get sourceType() { return this._sourceType; }
+ get sourceURL() { return this._sourceURL; }
+ get sourceMappingURL() { return this._sourceMappingURL; }
+ get injected() { return this._injected; }
+
+ get contentIdentifier()
+ {
+ if (this._url)
+ return this._url;
+
+ if (!this._sourceURL)
+ return null;
+
+ // Since reused content identifiers can cause breakpoints
+ // to show up in completely unrelated files, sourceURLs should
+ // be unique where possible. The checks below exclude cases
+ // where sourceURLs are intentionally reused and we would never
+ // expect a breakpoint to be persisted across sessions.
+ if (isWebInspectorConsoleEvaluationScript(this._sourceURL))
+ return null;
+
+ if (isWebInspectorInternalScript(this._sourceURL))
+ return null;
+
+ return this._sourceURL;
+ }
+
+ get urlComponents()
+ {
+ if (!this._urlComponents)
+ this._urlComponents = parseURL(this._url);
+ return this._urlComponents;
+ }
+
+ get mimeType()
+ {
+ return this._resource.mimeType;
+ }
+
+ get displayName()
+ {
+ if (this._url && !this._dynamicallyAddedScriptElement)
+ return WebInspector.displayNameForURL(this._url, this.urlComponents);
+
+ if (isWebInspectorConsoleEvaluationScript(this._sourceURL)) {
+ console.assert(this._uniqueDisplayNameNumber);
+ return WebInspector.UIString("Console Evaluation %d").format(this._uniqueDisplayNameNumber);
+ }
+
+ if (this._sourceURL) {
+ if (!this._sourceURLComponents)
+ this._sourceURLComponents = parseURL(this._sourceURL);
+ return WebInspector.displayNameForURL(this._sourceURL, this._sourceURLComponents);
+ }
+
+ if (this._dynamicallyAddedScriptElement)
+ return WebInspector.UIString("Script Element %d").format(this._dynamicallyAddedScriptElementNumber);
+
+ // Assign a unique number to the script object so it will stay the same.
+ if (!this._uniqueDisplayNameNumber)
+ this._uniqueDisplayNameNumber = this.constructor._nextUniqueDisplayNameNumber++;
+
+ return WebInspector.UIString("Anonymous Script %d").format(this._uniqueDisplayNameNumber);
+ }
+
+ get displayURL()
+ {
+ const isMultiLine = true;
+ const dataURIMaxSize = 64;
+
+ if (this._url)
+ return WebInspector.truncateURL(this._url, isMultiLine, dataURIMaxSize);
+ if (this._sourceURL)
+ return WebInspector.truncateURL(this._sourceURL, isMultiLine, dataURIMaxSize);
+ return null;
+ }
+
+ get dynamicallyAddedScriptElement()
+ {
+ return this._dynamicallyAddedScriptElement;
+ }
+
+ get anonymous()
+ {
+ return !this._resource && !this._url && !this._sourceURL;
+ }
+
+ get resource()
+ {
+ return this._resource;
+ }
+
+ get scriptSyntaxTree()
+ {
+ return this._scriptSyntaxTree;
+ }
+
+ isMainResource()
+ {
+ return this._target.mainResource === this;
+ }
+
+ requestContentFromBackend()
+ {
+ if (!this._id) {
+ // There is no identifier to request content with. Return false to cause the
+ // pending callbacks to get null content.
+ return Promise.reject(new Error("There is no identifier to request content with."));
+ }
+
+ return this._target.DebuggerAgent.getScriptSource(this._id);
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.Script.URLCookieKey] = this.url;
+ cookie[WebInspector.Script.DisplayNameCookieKey] = this.displayName;
+ }
+
+ requestScriptSyntaxTree(callback)
+ {
+ if (this._scriptSyntaxTree) {
+ setTimeout(() => { callback(this._scriptSyntaxTree); }, 0);
+ return;
+ }
+
+ var makeSyntaxTreeAndCallCallback = (content) => {
+ this._makeSyntaxTree(content);
+ callback(this._scriptSyntaxTree);
+ };
+
+ var content = this.content;
+ if (!content && this._resource && this._resource.type === WebInspector.Resource.Type.Script && this._resource.finished)
+ content = this._resource.content;
+ if (content) {
+ setTimeout(makeSyntaxTreeAndCallCallback, 0, content);
+ return;
+ }
+
+ this.requestContent().then(function(parameters) {
+ makeSyntaxTreeAndCallCallback(parameters.sourceCode.content);
+ }).catch(function(error) {
+ makeSyntaxTreeAndCallCallback(null);
+ });
+ }
+
+ // Private
+
+ _resolveResource()
+ {
+ // FIXME: We should be able to associate a Script with a Resource through identifiers,
+ // we shouldn't need to lookup by URL, which is not safe with frames, where there might
+ // be multiple resources with the same URL.
+ // <rdar://problem/13373951> Scripts should be able to associate directly with a Resource
+
+ // No URL, no resource.
+ if (!this._url)
+ return null;
+
+ let resolver = WebInspector.frameResourceManager;
+ if (this._target !== WebInspector.mainTarget)
+ resolver = this._target.resourceCollection;
+
+ try {
+ // Try with the Script's full URL.
+ let resource = resolver.resourceForURL(this._url);
+ if (resource)
+ return resource;
+
+ // Try with the Script's full decoded URL.
+ let decodedURL = decodeURI(this._url);
+ if (decodedURL !== this._url) {
+ resource = resolver.resourceForURL(decodedURL);
+ if (resource)
+ return resource;
+ }
+
+ // Next try removing any fragment in the original URL.
+ let urlWithoutFragment = removeURLFragment(this._url);
+ if (urlWithoutFragment !== this._url) {
+ resource = resolver.resourceForURL(urlWithoutFragment);
+ if (resource)
+ return resource;
+ }
+
+ // Finally try removing any fragment in the decoded URL.
+ let decodedURLWithoutFragment = removeURLFragment(decodedURL);
+ if (decodedURLWithoutFragment !== decodedURL) {
+ resource = resolver.resourceForURL(decodedURLWithoutFragment);
+ if (resource)
+ return resource;
+ }
+ } catch (e) {
+ // Ignore possible URIErrors.
+ }
+
+ return null;
+ }
+
+ _makeSyntaxTree(sourceText)
+ {
+ if (this._scriptSyntaxTree || !sourceText)
+ return;
+
+ this._scriptSyntaxTree = new WebInspector.ScriptSyntaxTree(sourceText, this);
+ }
+};
+
+WebInspector.Script.SourceType = {
+ Program: "script-source-type-program",
+ Module: "script-source-type-module",
+};
+
+WebInspector.Script.TypeIdentifier = "script";
+WebInspector.Script.URLCookieKey = "script-url";
+WebInspector.Script.DisplayNameCookieKey = "script-display-name";
+
+WebInspector.Script._nextUniqueDisplayNameNumber = 1;
+WebInspector.Script._nextUniqueConsoleDisplayNameNumber = 1;
diff --git a/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js b/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js
new file mode 100644
index 000000000..cea34da36
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ScriptInstrument = class ScriptInstrument extends WebInspector.Instrument
+{
+ // Protected
+
+ get timelineRecordType()
+ {
+ return WebInspector.TimelineRecord.Type.Script;
+ }
+
+ startInstrumentation(initiatedByBackend)
+ {
+ // COMPATIBILITY (iOS 9): Legacy backends did not have ScriptProfilerAgent. They use TimelineAgent.
+ if (!window.ScriptProfilerAgent) {
+ super.startInstrumentation();
+ return;
+ }
+
+ // FIXME: Make this some UI visible option.
+ const includeSamples = true;
+
+ if (!initiatedByBackend)
+ ScriptProfilerAgent.startTracking(includeSamples);
+ }
+
+ stopInstrumentation(initiatedByBackend)
+ {
+ // COMPATIBILITY (iOS 9): Legacy backends did not have ScriptProfilerAgent. They use TimelineAgent.
+ if (!window.ScriptProfilerAgent) {
+ super.stopInstrumentation();
+ return;
+ }
+
+ if (!initiatedByBackend)
+ ScriptProfilerAgent.stopTracking();
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ScriptSyntaxTree.js b/Source/WebInspectorUI/UserInterface/Models/ScriptSyntaxTree.js
new file mode 100644
index 000000000..ab69285f2
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ScriptSyntaxTree.js
@@ -0,0 +1,1146 @@
+/*
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ScriptSyntaxTree = class ScriptSyntaxTree extends WebInspector.Object
+{
+ constructor(sourceText, script)
+ {
+ super();
+
+ console.assert(script && script instanceof WebInspector.Script, script);
+
+ this._script = script;
+
+ try {
+ let sourceType = this._script.sourceType === WebInspector.Script.SourceType.Module ? "module" : "script";
+ let esprimaSyntaxTree = esprima.parse(sourceText, {range: true, sourceType});
+ this._syntaxTree = this._createInternalSyntaxTree(esprimaSyntaxTree);
+ this._parsedSuccessfully = true;
+ } catch (error) {
+ this._parsedSuccessfully = false;
+ this._syntaxTree = null;
+ console.error("Couldn't parse JavaScript File: " + script.url, error);
+ }
+ }
+
+ // Public
+
+ get parsedSuccessfully()
+ {
+ return this._parsedSuccessfully;
+ }
+
+ forEachNode(callback)
+ {
+ console.assert(this._parsedSuccessfully);
+ if (!this._parsedSuccessfully)
+ return;
+
+ this._recurse(this._syntaxTree, callback, this._defaultParserState());
+ }
+
+ filter(predicate, startNode)
+ {
+ console.assert(startNode && this._parsedSuccessfully);
+ if (!this._parsedSuccessfully)
+ return [];
+
+ var nodes = [];
+ function filter(node, state)
+ {
+ if (predicate(node))
+ nodes.push(node);
+ else
+ state.skipChildNodes = true;
+ }
+
+ this._recurse(startNode, filter, this._defaultParserState());
+
+ return nodes;
+ }
+
+ containersOfOffset(offset)
+ {
+ console.assert(this._parsedSuccessfully);
+ if (!this._parsedSuccessfully)
+ return [];
+
+ let allNodes = [];
+ const start = 0;
+ const end = 1;
+
+ this.forEachNode((node, state) => {
+ if (node.range[end] < offset)
+ state.skipChildNodes = true;
+ if (node.range[start] > offset)
+ state.shouldStopEarly = true;
+ if (node.range[start] <= offset && node.range[end] >= offset)
+ allNodes.push(node);
+ });
+
+ return allNodes;
+ }
+
+ filterByRange(startOffset, endOffset)
+ {
+ console.assert(this._parsedSuccessfully);
+ if (!this._parsedSuccessfully)
+ return [];
+
+ var allNodes = [];
+ var start = 0;
+ var end = 1;
+ function filterForNodesInRange(node, state)
+ {
+ // program start range program end
+ // [ [ ] ]
+ // [ ] [ [ ] ] [ ]
+
+ // If a node's range ends before the range we're interested in starts, we don't need to search any of its
+ // enclosing ranges, because, by definition, those enclosing ranges are contained within this node's range.
+ if (node.range[end] < startOffset)
+ state.skipChildNodes = true;
+
+ // We are only interested in nodes whose start position is within our range.
+ if (startOffset <= node.range[start] && node.range[start] <= endOffset)
+ allNodes.push(node);
+
+ // Once we see nodes that start beyond our range, we can quit traversing the AST. We can do this safely
+ // because we know the AST is traversed using depth first search, so it will traverse into enclosing ranges
+ // before it traverses into adjacent ranges.
+ if (node.range[start] > endOffset)
+ state.shouldStopEarly = true;
+ }
+
+ this.forEachNode(filterForNodesInRange);
+
+ return allNodes;
+ }
+
+ containsNonEmptyReturnStatement(startNode)
+ {
+ console.assert(startNode && this._parsedSuccessfully);
+ if (!this._parsedSuccessfully)
+ return false;
+
+ if (startNode.attachments._hasNonEmptyReturnStatement !== undefined)
+ return startNode.attachments._hasNonEmptyReturnStatement;
+
+ function removeFunctionsFilter(node)
+ {
+ return node.type !== WebInspector.ScriptSyntaxTree.NodeType.FunctionExpression
+ && node.type !== WebInspector.ScriptSyntaxTree.NodeType.FunctionDeclaration
+ && node.type !== WebInspector.ScriptSyntaxTree.NodeType.ArrowFunctionExpression;
+ }
+
+ var nodes = this.filter(removeFunctionsFilter, startNode);
+ var hasNonEmptyReturnStatement = false;
+ var returnStatementType = WebInspector.ScriptSyntaxTree.NodeType.ReturnStatement;
+ for (var node of nodes) {
+ if (node.type === returnStatementType && node.argument) {
+ hasNonEmptyReturnStatement = true;
+ break;
+ }
+ }
+
+ startNode.attachments._hasNonEmptyReturnStatement = hasNonEmptyReturnStatement;
+
+ return hasNonEmptyReturnStatement;
+ }
+
+ static functionReturnDivot(node)
+ {
+ console.assert(node.type === WebInspector.ScriptSyntaxTree.NodeType.FunctionDeclaration || node.type === WebInspector.ScriptSyntaxTree.NodeType.FunctionExpression || node.type === WebInspector.ScriptSyntaxTree.NodeType.MethodDefinition || node.type === WebInspector.ScriptSyntaxTree.NodeType.ArrowFunctionExpression);
+
+ // COMPATIBILITY (iOS 9): Legacy Backends view the return type as being the opening "{" of the function body.
+ // After iOS 9, this is to move to the start of the function statement/expression. See below:
+ // FIXME: Need a better way to determine backend versions. Using DOM.pseudoElement because that was added after iOS 9.
+ if (!DOMAgent.hasEvent("pseudoElementAdded"))
+ return node.body.range[0];
+
+ // "f" in "function". "s" in "set". "g" in "get". First letter in any method name for classes and object literals.
+ // The "[" for computed methods in classes and object literals.
+ return node.typeProfilingReturnDivot;
+ }
+
+ updateTypes(nodesToUpdate, callback)
+ {
+ console.assert(RuntimeAgent.getRuntimeTypesForVariablesAtOffsets);
+ console.assert(Array.isArray(nodesToUpdate) && this._parsedSuccessfully);
+
+ if (!this._parsedSuccessfully)
+ return;
+
+ var allRequests = [];
+ var allRequestNodes = [];
+ var sourceID = this._script.id;
+
+ for (var node of nodesToUpdate) {
+ switch (node.type) {
+ case WebInspector.ScriptSyntaxTree.NodeType.FunctionDeclaration:
+ case WebInspector.ScriptSyntaxTree.NodeType.FunctionExpression:
+ case WebInspector.ScriptSyntaxTree.NodeType.ArrowFunctionExpression:
+ for (var param of node.params) {
+ for (var identifier of this._gatherIdentifiersInDeclaration(param)) {
+ allRequests.push({
+ typeInformationDescriptor: WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
+ sourceID,
+ divot: identifier.range[0]
+ });
+ allRequestNodes.push(identifier);
+ }
+ }
+
+ allRequests.push({
+ typeInformationDescriptor: WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.FunctionReturn,
+ sourceID,
+ divot: WebInspector.ScriptSyntaxTree.functionReturnDivot(node)
+ });
+ allRequestNodes.push(node);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.VariableDeclarator:
+ for (var identifier of this._gatherIdentifiersInDeclaration(node.id)) {
+ allRequests.push({
+ typeInformationDescriptor: WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
+ sourceID,
+ divot: identifier.range[0]
+ });
+ allRequestNodes.push(identifier);
+ }
+ break;
+ }
+ }
+
+ console.assert(allRequests.length === allRequestNodes.length);
+
+ function handleTypes(error, typeInformationArray)
+ {
+ if (error)
+ return;
+
+ console.assert(typeInformationArray.length === allRequests.length);
+
+ for (var i = 0; i < typeInformationArray.length; i++) {
+ var node = allRequestNodes[i];
+ var typeInformation = WebInspector.TypeDescription.fromPayload(typeInformationArray[i]);
+ if (allRequests[i].typeInformationDescriptor === WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.FunctionReturn)
+ node.attachments.returnTypes = typeInformation;
+ else
+ node.attachments.types = typeInformation;
+ }
+
+ callback(allRequestNodes);
+ }
+
+ this._script.target.RuntimeAgent.getRuntimeTypesForVariablesAtOffsets(allRequests, handleTypes);
+ }
+
+ // Private
+
+ _gatherIdentifiersInDeclaration(node)
+ {
+ function gatherIdentifiers(node)
+ {
+ switch (node.type) {
+ case WebInspector.ScriptSyntaxTree.NodeType.Identifier:
+ return [node];
+ case WebInspector.ScriptSyntaxTree.NodeType.Property:
+ return gatherIdentifiers(node.value);
+ case WebInspector.ScriptSyntaxTree.NodeType.ObjectPattern:
+ var identifiers = [];
+ for (var property of node.properties) {
+ for (var identifier of gatherIdentifiers(property))
+ identifiers.push(identifier);
+ }
+ return identifiers;
+ case WebInspector.ScriptSyntaxTree.NodeType.ArrayPattern:
+ var identifiers = [];
+ for (var element of node.elements) {
+ for (var identifier of gatherIdentifiers(element))
+ identifiers.push(identifier);
+ }
+ return identifiers;
+ case WebInspector.ScriptSyntaxTree.NodeType.AssignmentPattern:
+ return gatherIdentifiers(node.left);
+ case WebInspector.ScriptSyntaxTree.NodeType.RestElement:
+ case WebInspector.ScriptSyntaxTree.NodeType.RestProperty:
+ return gatherIdentifiers(node.argument);
+ default:
+ console.assert(false, "Unexpected node type in variable declarator: " + node.type);
+ return [];
+ }
+ }
+
+ console.assert(node.type === WebInspector.ScriptSyntaxTree.NodeType.Identifier || node.type === WebInspector.ScriptSyntaxTree.NodeType.ObjectPattern || node.type === WebInspector.ScriptSyntaxTree.NodeType.ArrayPattern || node.type === WebInspector.ScriptSyntaxTree.NodeType.RestElement || node.type === WebInspector.ScriptSyntaxTree.NodeType.RestProperty);
+
+ return gatherIdentifiers(node);
+ }
+
+ _defaultParserState()
+ {
+ return {
+ shouldStopEarly: false,
+ skipChildNodes: false
+ };
+ }
+
+ _recurse(node, callback, state)
+ {
+ if (!node)
+ return;
+
+ if (state.shouldStopEarly || state.skipChildNodes)
+ return;
+
+ callback(node, state);
+
+ switch (node.type) {
+ case WebInspector.ScriptSyntaxTree.NodeType.AssignmentExpression:
+ this._recurse(node.left, callback, state);
+ this._recurse(node.right, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ArrayExpression:
+ case WebInspector.ScriptSyntaxTree.NodeType.ArrayPattern:
+ this._recurseArray(node.elements, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.AssignmentPattern:
+ this._recurse(node.left, callback, state);
+ this._recurse(node.right, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.AwaitExpression:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.BlockStatement:
+ this._recurseArray(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.BinaryExpression:
+ this._recurse(node.left, callback, state);
+ this._recurse(node.right, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.BreakStatement:
+ this._recurse(node.label, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.CatchClause:
+ this._recurse(node.param, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.CallExpression:
+ this._recurse(node.callee, callback, state);
+ this._recurseArray(node.arguments, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ClassBody:
+ this._recurseArray(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ClassDeclaration:
+ case WebInspector.ScriptSyntaxTree.NodeType.ClassExpression:
+ this._recurse(node.id, callback, state);
+ this._recurse(node.superClass, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ContinueStatement:
+ this._recurse(node.label, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.DoWhileStatement:
+ this._recurse(node.body, callback, state);
+ this._recurse(node.test, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ExpressionStatement:
+ this._recurse(node.expression, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ForStatement:
+ this._recurse(node.init, callback, state);
+ this._recurse(node.test, callback, state);
+ this._recurse(node.update, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ForInStatement:
+ case WebInspector.ScriptSyntaxTree.NodeType.ForOfStatement:
+ this._recurse(node.left, callback, state);
+ this._recurse(node.right, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.FunctionDeclaration:
+ case WebInspector.ScriptSyntaxTree.NodeType.FunctionExpression:
+ case WebInspector.ScriptSyntaxTree.NodeType.ArrowFunctionExpression:
+ this._recurse(node.id, callback, state);
+ this._recurseArray(node.params, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.IfStatement:
+ this._recurse(node.test, callback, state);
+ this._recurse(node.consequent, callback, state);
+ this._recurse(node.alternate, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.LabeledStatement:
+ this._recurse(node.label, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.LogicalExpression:
+ this._recurse(node.left, callback, state);
+ this._recurse(node.right, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.MemberExpression:
+ this._recurse(node.object, callback, state);
+ this._recurse(node.property, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.MethodDefinition:
+ this._recurse(node.key, callback, state);
+ this._recurse(node.value, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.NewExpression:
+ this._recurse(node.callee, callback, state);
+ this._recurseArray(node.arguments, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ObjectExpression:
+ case WebInspector.ScriptSyntaxTree.NodeType.ObjectPattern:
+ this._recurseArray(node.properties, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.Program:
+ this._recurseArray(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.Property:
+ this._recurse(node.key, callback, state);
+ this._recurse(node.value, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.RestElement:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.RestProperty:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ReturnStatement:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.SequenceExpression:
+ this._recurseArray(node.expressions, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.SpreadElement:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.SpreadProperty:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.SwitchStatement:
+ this._recurse(node.discriminant, callback, state);
+ this._recurseArray(node.cases, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.SwitchCase:
+ this._recurse(node.test, callback, state);
+ this._recurseArray(node.consequent, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ConditionalExpression:
+ this._recurse(node.test, callback, state);
+ this._recurse(node.consequent, callback, state);
+ this._recurse(node.alternate, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.TaggedTemplateExpression:
+ this._recurse(node.tag, callback, state);
+ this._recurse(node.quasi, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.TemplateLiteral:
+ this._recurseArray(node.quasis, callback, state);
+ this._recurseArray(node.expressions, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ThrowStatement:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.TryStatement:
+ this._recurse(node.block, callback, state);
+ this._recurse(node.handler, callback, state);
+ this._recurse(node.finalizer, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.UnaryExpression:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.UpdateExpression:
+ this._recurse(node.argument, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.VariableDeclaration:
+ this._recurseArray(node.declarations, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.VariableDeclarator:
+ this._recurse(node.id, callback, state);
+ this._recurse(node.init, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.WhileStatement:
+ this._recurse(node.test, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.WithStatement:
+ this._recurse(node.object, callback, state);
+ this._recurse(node.body, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.YieldExpression:
+ this._recurse(node.argument, callback, state);
+ break;
+
+ // Modules.
+
+ case WebInspector.ScriptSyntaxTree.NodeType.ExportAllDeclaration:
+ this._recurse(node.source, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ExportNamedDeclaration:
+ this._recurse(node.declaration, callback, state);
+ this._recurseArray(node.specifiers, callback, state);
+ this._recurse(node.source, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ExportDefaultDeclaration:
+ this._recurse(node.declaration, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ExportSpecifier:
+ this._recurse(node.local, callback, state);
+ this._recurse(node.exported, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ImportDeclaration:
+ this._recurseArray(node.specifiers, callback, state);
+ this._recurse(node.source, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ImportDefaultSpecifier:
+ this._recurse(node.local, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ImportNamespaceSpecifier:
+ this._recurse(node.local, callback, state);
+ break;
+ case WebInspector.ScriptSyntaxTree.NodeType.ImportSpecifier:
+ this._recurse(node.imported, callback, state);
+ this._recurse(node.local, callback, state);
+ break;
+
+ // All the leaf nodes go here.
+ case WebInspector.ScriptSyntaxTree.NodeType.DebuggerStatement:
+ case WebInspector.ScriptSyntaxTree.NodeType.EmptyStatement:
+ case WebInspector.ScriptSyntaxTree.NodeType.Identifier:
+ case WebInspector.ScriptSyntaxTree.NodeType.Import:
+ case WebInspector.ScriptSyntaxTree.NodeType.Literal:
+ case WebInspector.ScriptSyntaxTree.NodeType.MetaProperty:
+ case WebInspector.ScriptSyntaxTree.NodeType.Super:
+ case WebInspector.ScriptSyntaxTree.NodeType.ThisExpression:
+ case WebInspector.ScriptSyntaxTree.NodeType.TemplateElement:
+ break;
+ }
+
+ state.skipChildNodes = false;
+ }
+
+ _recurseArray(array, callback, state)
+ {
+ for (var node of array)
+ this._recurse(node, callback, state);
+ }
+
+ // This function translates from esprima's Abstract Syntax Tree to ours.
+ // Mostly, this is just the identity function. We've added an extra typeProfilingReturnDivot property for functions/methods.
+ // Our AST complies with the Mozilla parser API:
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API
+ _createInternalSyntaxTree(node)
+ {
+ if (!node)
+ return null;
+
+ var result = null;
+ switch (node.type) {
+ case "ArrayExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ArrayExpression,
+ elements: node.elements.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "ArrayPattern":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ArrayPattern,
+ elements: node.elements.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "ArrowFunctionExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ArrowFunctionExpression,
+ id: this._createInternalSyntaxTree(node.id),
+ params: node.params.map(this._createInternalSyntaxTree, this),
+ body: this._createInternalSyntaxTree(node.body),
+ generator: node.generator,
+ expression: node.expression, // Boolean indicating if the body a single expression or a block statement.
+ async: node.async,
+ typeProfilingReturnDivot: node.range[0]
+ };
+ break;
+ case "AssignmentExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.AssignmentExpression,
+ operator: node.operator,
+ left: this._createInternalSyntaxTree(node.left),
+ right: this._createInternalSyntaxTree(node.right)
+ };
+ break;
+ case "AssignmentPattern":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.AssignmentPattern,
+ left: this._createInternalSyntaxTree(node.left),
+ right: this._createInternalSyntaxTree(node.right),
+ };
+ break;
+ case "AwaitExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.AwaitExpression,
+ argument: this._createInternalSyntaxTree(node.argument),
+ };
+ break;
+ case "BlockStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.BlockStatement,
+ body: node.body.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "BinaryExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.BinaryExpression,
+ operator: node.operator,
+ left: this._createInternalSyntaxTree(node.left),
+ right: this._createInternalSyntaxTree(node.right)
+ };
+ break;
+ case "BreakStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.BreakStatement,
+ label: this._createInternalSyntaxTree(node.label)
+ };
+ break;
+ case "CallExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.CallExpression,
+ callee: this._createInternalSyntaxTree(node.callee),
+ arguments: node.arguments.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "CatchClause":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.CatchClause,
+ param: this._createInternalSyntaxTree(node.param),
+ body: this._createInternalSyntaxTree(node.body)
+ };
+ break;
+ case "ClassBody":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ClassBody,
+ body: node.body.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "ClassDeclaration":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ClassDeclaration,
+ id: this._createInternalSyntaxTree(node.id),
+ superClass: this._createInternalSyntaxTree(node.superClass),
+ body: this._createInternalSyntaxTree(node.body),
+ };
+ break;
+ case "ClassExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ClassExpression,
+ id: this._createInternalSyntaxTree(node.id),
+ superClass: this._createInternalSyntaxTree(node.superClass),
+ body: this._createInternalSyntaxTree(node.body),
+ };
+ break;
+ case "ConditionalExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ConditionalExpression,
+ test: this._createInternalSyntaxTree(node.test),
+ consequent: this._createInternalSyntaxTree(node.consequent),
+ alternate: this._createInternalSyntaxTree(node.alternate)
+ };
+ break;
+ case "ContinueStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ContinueStatement,
+ label: this._createInternalSyntaxTree(node.label)
+ };
+ break;
+ case "DoWhileStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.DoWhileStatement,
+ body: this._createInternalSyntaxTree(node.body),
+ test: this._createInternalSyntaxTree(node.test)
+ };
+ break;
+ case "DebuggerStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.DebuggerStatement
+ };
+ break;
+ case "EmptyStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.EmptyStatement
+ };
+ break;
+ case "ExpressionStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ExpressionStatement,
+ expression: this._createInternalSyntaxTree(node.expression)
+ };
+ break;
+ case "ForStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ForStatement,
+ init: this._createInternalSyntaxTree(node.init),
+ test: this._createInternalSyntaxTree(node.test),
+ update: this._createInternalSyntaxTree(node.update),
+ body: this._createInternalSyntaxTree(node.body)
+ };
+ break;
+ case "ForInStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ForInStatement,
+ left: this._createInternalSyntaxTree(node.left),
+ right: this._createInternalSyntaxTree(node.right),
+ body: this._createInternalSyntaxTree(node.body)
+ };
+ break;
+ case "ForOfStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ForOfStatement,
+ left: this._createInternalSyntaxTree(node.left),
+ right: this._createInternalSyntaxTree(node.right),
+ body: this._createInternalSyntaxTree(node.body)
+ };
+ break;
+ case "FunctionDeclaration":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.FunctionDeclaration,
+ id: this._createInternalSyntaxTree(node.id),
+ params: node.params.map(this._createInternalSyntaxTree, this),
+ body: this._createInternalSyntaxTree(node.body),
+ generator: node.generator,
+ async: node.async,
+ typeProfilingReturnDivot: node.range[0]
+ };
+ break;
+ case "FunctionExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.FunctionExpression,
+ id: this._createInternalSyntaxTree(node.id),
+ params: node.params.map(this._createInternalSyntaxTree, this),
+ body: this._createInternalSyntaxTree(node.body),
+ generator: node.generator,
+ async: node.async,
+ typeProfilingReturnDivot: node.range[0] // This may be overridden in the Property AST node.
+ };
+ break;
+ case "Identifier":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.Identifier,
+ name: node.name
+ };
+ break;
+ case "IfStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.IfStatement,
+ test: this._createInternalSyntaxTree(node.test),
+ consequent: this._createInternalSyntaxTree(node.consequent),
+ alternate: this._createInternalSyntaxTree(node.alternate)
+ };
+ break;
+ case "Literal":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.Literal,
+ value: node.value,
+ raw: node.raw
+ };
+ break;
+ case "LabeledStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.LabeledStatement,
+ label: this._createInternalSyntaxTree(node.label),
+ body: this._createInternalSyntaxTree(node.body)
+ };
+ break;
+ case "LogicalExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.LogicalExpression,
+ left: this._createInternalSyntaxTree(node.left),
+ right: this._createInternalSyntaxTree(node.right),
+ operator: node.operator
+ };
+ break;
+ case "MemberExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.MemberExpression,
+ object: this._createInternalSyntaxTree(node.object),
+ property: this._createInternalSyntaxTree(node.property),
+ computed: node.computed
+ };
+ break;
+ case "MetaProperty":
+ // i.e: new.target produces {meta: "new", property: "target"}
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.MetaProperty,
+ meta: this._createInternalSyntaxTree(node.meta),
+ property: this._createInternalSyntaxTree(node.property),
+ };
+ break;
+ case "MethodDefinition":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.MethodDefinition,
+ key: this._createInternalSyntaxTree(node.key),
+ value: this._createInternalSyntaxTree(node.value),
+ computed: node.computed,
+ kind: node.kind,
+ static: node.static
+ };
+ result.value.typeProfilingReturnDivot = node.range[0]; // "g" in "get" or "s" in "set" or "[" in "['computed']" or "m" in "methodName".
+ break;
+ case "NewExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.NewExpression,
+ callee: this._createInternalSyntaxTree(node.callee),
+ arguments: node.arguments.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "ObjectExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ObjectExpression,
+ properties: node.properties.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "ObjectPattern":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ObjectPattern,
+ properties: node.properties.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "Program":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.Program,
+ sourceType: node.sourceType,
+ body: node.body.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "Property":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.Property,
+ key: this._createInternalSyntaxTree(node.key),
+ value: this._createInternalSyntaxTree(node.value),
+ kind: node.kind,
+ method: node.method,
+ computed: node.computed
+ };
+ if (result.kind === "get" || result.kind === "set" || result.method)
+ result.value.typeProfilingReturnDivot = node.range[0]; // "g" in "get" or "s" in "set" or "[" in "['computed']" method or "m" in "methodName".
+ break;
+ case "RestElement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.RestElement,
+ argument: this._createInternalSyntaxTree(node.argument)
+ };
+ break;
+ case "RestProperty":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.RestProperty,
+ argument: this._createInternalSyntaxTree(node.argument),
+ };
+ break;
+ case "ReturnStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ReturnStatement,
+ argument: this._createInternalSyntaxTree(node.argument)
+ };
+ break;
+ case "SequenceExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.SequenceExpression,
+ expressions: node.expressions.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "SpreadElement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.SpreadElement,
+ argument: this._createInternalSyntaxTree(node.argument),
+ };
+ break;
+ case "SpreadProperty":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.SpreadProperty,
+ argument: this._createInternalSyntaxTree(node.argument),
+ };
+ break;
+ case "Super":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.Super
+ };
+ break;
+ case "SwitchStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.SwitchStatement,
+ discriminant: this._createInternalSyntaxTree(node.discriminant),
+ cases: node.cases.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "SwitchCase":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.SwitchCase,
+ test: this._createInternalSyntaxTree(node.test),
+ consequent: node.consequent.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "TaggedTemplateExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.TaggedTemplateExpression,
+ tag: this._createInternalSyntaxTree(node.tag),
+ quasi: this._createInternalSyntaxTree(node.quasi)
+ };
+ break;
+ case "TemplateElement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.TemplateElement,
+ value: node.value,
+ tail: node.tail
+ };
+ break;
+ case "TemplateLiteral":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.TemplateLiteral,
+ quasis: node.quasis.map(this._createInternalSyntaxTree, this),
+ expressions: node.expressions.map(this._createInternalSyntaxTree, this)
+ };
+ break;
+ case "ThisExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ThisExpression
+ };
+ break;
+ case "ThrowStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ThrowStatement,
+ argument: this._createInternalSyntaxTree(node.argument)
+ };
+ break;
+ case "TryStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.TryStatement,
+ block: this._createInternalSyntaxTree(node.block),
+ handler: this._createInternalSyntaxTree(node.handler),
+ finalizer: this._createInternalSyntaxTree(node.finalizer)
+ };
+ break;
+ case "UnaryExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.UnaryExpression,
+ operator: node.operator,
+ argument: this._createInternalSyntaxTree(node.argument)
+ };
+ break;
+ case "UpdateExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.UpdateExpression,
+ operator: node.operator,
+ prefix: node.prefix,
+ argument: this._createInternalSyntaxTree(node.argument)
+ };
+ break;
+ case "VariableDeclaration":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.VariableDeclaration,
+ declarations: node.declarations.map(this._createInternalSyntaxTree, this),
+ kind: node.kind
+ };
+ break;
+ case "VariableDeclarator":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.VariableDeclarator,
+ id: this._createInternalSyntaxTree(node.id),
+ init: this._createInternalSyntaxTree(node.init)
+ };
+ break;
+ case "WhileStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.WhileStatement,
+ test: this._createInternalSyntaxTree(node.test),
+ body: this._createInternalSyntaxTree(node.body)
+ };
+ break;
+ case "WithStatement":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.WithStatement,
+ object: this._createInternalSyntaxTree(node.object),
+ body: this._createInternalSyntaxTree(node.body)
+ };
+ break;
+ case "YieldExpression":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.YieldExpression,
+ argument: this._createInternalSyntaxTree(node.argument),
+ delegate: node.delegate
+ };
+ break;
+
+ // Modules.
+
+ case "ExportAllDeclaration":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ExportAllDeclaration,
+ source: this._createInternalSyntaxTree(node.source),
+ };
+ break;
+ case "ExportNamedDeclaration":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ExportNamedDeclaration,
+ declaration: this._createInternalSyntaxTree(node.declaration),
+ specifiers: node.specifiers.map(this._createInternalSyntaxTree, this),
+ source: this._createInternalSyntaxTree(node.source),
+ };
+ break;
+ case "ExportDefaultDeclaration":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ExportDefaultDeclaration,
+ declaration: this._createInternalSyntaxTree(node.declaration),
+ };
+ break;
+ case "ExportSpecifier":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ExportSpecifier,
+ local: this._createInternalSyntaxTree(node.local),
+ exported: this._createInternalSyntaxTree(node.exported),
+ };
+ break;
+ case "Import":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.Import,
+ };
+ break;
+ case "ImportDeclaration":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ImportDeclaration,
+ specifiers: node.specifiers.map(this._createInternalSyntaxTree, this),
+ source: this._createInternalSyntaxTree(node.source),
+ };
+ break;
+ case "ImportDefaultSpecifier":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ImportDefaultSpecifier,
+ local: this._createInternalSyntaxTree(node.local),
+ };
+ break;
+ case "ImportNamespaceSpecifier":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ImportNamespaceSpecifier,
+ local: this._createInternalSyntaxTree(node.local),
+ };
+ break;
+ case "ImportSpecifier":
+ result = {
+ type: WebInspector.ScriptSyntaxTree.NodeType.ImportSpecifier,
+ imported: this._createInternalSyntaxTree(node.imported),
+ local: this._createInternalSyntaxTree(node.local),
+ };
+ break;
+
+ default:
+ console.error("Unsupported Syntax Tree Node: " + node.type, node);
+ return null;
+ }
+
+ result.range = node.range;
+ // This is an object for which you can add fields to an AST node without worrying about polluting the syntax-related fields of the node.
+ result.attachments = {};
+
+ return result;
+ }
+};
+
+// This should be kept in sync with an enum in JavaSciptCore/runtime/TypeProfiler.h
+WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor = {
+ NormalExpression: 1,
+ FunctionReturn: 2
+};
+
+WebInspector.ScriptSyntaxTree.NodeType = {
+ ArrayExpression: Symbol("array-expression"),
+ ArrayPattern: Symbol("array-pattern"),
+ ArrowFunctionExpression: Symbol("arrow-function-expression"),
+ AssignmentExpression: Symbol("assignment-expression"),
+ AssignmentPattern: Symbol("assignment-pattern"),
+ AwaitExpression: Symbol("await-expression"),
+ BinaryExpression: Symbol("binary-expression"),
+ BlockStatement: Symbol("block-statement"),
+ BreakStatement: Symbol("break-statement"),
+ CallExpression: Symbol("call-expression"),
+ CatchClause: Symbol("catch-clause"),
+ ClassBody: Symbol("class-body"),
+ ClassDeclaration: Symbol("class-declaration"),
+ ClassExpression: Symbol("class-expression"),
+ ConditionalExpression: Symbol("conditional-expression"),
+ ContinueStatement: Symbol("continue-statement"),
+ DebuggerStatement: Symbol("debugger-statement"),
+ DoWhileStatement: Symbol("do-while-statement"),
+ EmptyStatement: Symbol("empty-statement"),
+ ExportAllDeclaration: Symbol("export-all-declaration"),
+ ExportDefaultDeclaration: Symbol("export-default-declaration"),
+ ExportNamedDeclaration: Symbol("export-named-declaration"),
+ ExportSpecifier: Symbol("export-specifier"),
+ ExpressionStatement: Symbol("expression-statement"),
+ ForInStatement: Symbol("for-in-statement"),
+ ForOfStatement: Symbol("for-of-statement"),
+ ForStatement: Symbol("for-statement"),
+ FunctionDeclaration: Symbol("function-declaration"),
+ FunctionExpression: Symbol("function-expression"),
+ Identifier: Symbol("identifier"),
+ IfStatement: Symbol("if-statement"),
+ Import: Symbol("import"),
+ ImportDeclaration: Symbol("import-declaration"),
+ ImportDefaultSpecifier: Symbol("import-default-specifier"),
+ ImportNamespaceSpecifier: Symbol("import-namespace-specifier"),
+ ImportSpecifier: Symbol("import-specifier"),
+ LabeledStatement: Symbol("labeled-statement"),
+ Literal: Symbol("literal"),
+ LogicalExpression: Symbol("logical-expression"),
+ MemberExpression: Symbol("member-expression"),
+ MetaProperty: Symbol("meta-property"),
+ MethodDefinition: Symbol("method-definition"),
+ NewExpression: Symbol("new-expression"),
+ ObjectExpression: Symbol("object-expression"),
+ ObjectPattern: Symbol("object-pattern"),
+ Program: Symbol("program"),
+ Property: Symbol("property"),
+ RestElement: Symbol("rest-element"),
+ RestProperty: Symbol("rest-property"),
+ ReturnStatement: Symbol("return-statement"),
+ SequenceExpression: Symbol("sequence-expression"),
+ SpreadElement: Symbol("spread-element"),
+ SpreadProperty: Symbol("spread-property"),
+ Super: Symbol("super"),
+ SwitchCase: Symbol("switch-case"),
+ SwitchStatement: Symbol("switch-statement"),
+ TaggedTemplateExpression: Symbol("tagged-template-expression"),
+ TemplateElement: Symbol("template-element"),
+ TemplateLiteral: Symbol("template-literal"),
+ ThisExpression: Symbol("this-expression"),
+ ThrowStatement: Symbol("throw-statement"),
+ TryStatement: Symbol("try-statement"),
+ UnaryExpression: Symbol("unary-expression"),
+ UpdateExpression: Symbol("update-expression"),
+ VariableDeclaration: Symbol("variable-declaration"),
+ VariableDeclarator: Symbol("variable-declarator"),
+ WhileStatement: Symbol("while-statement"),
+ WithStatement: Symbol("with-statement"),
+ YieldExpression: Symbol("yield-expression"),
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js
new file mode 100644
index 000000000..4a4231d82
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ScriptTimelineRecord = class ScriptTimelineRecord extends WebInspector.TimelineRecord
+{
+ constructor(eventType, startTime, endTime, callFrames, sourceCodeLocation, details, profilePayload)
+ {
+ super(WebInspector.TimelineRecord.Type.Script, startTime, endTime, callFrames, sourceCodeLocation);
+
+ console.assert(eventType);
+
+ if (eventType in WebInspector.ScriptTimelineRecord.EventType)
+ eventType = WebInspector.ScriptTimelineRecord.EventType[eventType];
+
+ this._eventType = eventType;
+ this._details = details || "";
+ this._profilePayload = profilePayload || null;
+ this._profile = null;
+
+ // COMPATIBILITY(iOS 9): Before the ScriptProfilerAgent we did not have sample data. Return NaN to match old behavior.
+ if (!window.ScriptProfilerAgent)
+ this._callCountOrSamples = NaN;
+ else {
+ // NOTE: _callCountOrSamples is being treated as the number of samples.
+ this._callCountOrSamples = 0;
+ }
+ }
+
+ // Public
+
+ get eventType()
+ {
+ return this._eventType;
+ }
+
+ get details()
+ {
+ return this._details;
+ }
+
+ get profile()
+ {
+ this._initializeProfileFromPayload();
+ return this._profile;
+ }
+
+ get callCountOrSamples()
+ {
+ return this._callCountOrSamples;
+ }
+
+ isGarbageCollection()
+ {
+ return this._eventType === WebInspector.ScriptTimelineRecord.EventType.GarbageCollected;
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ super.saveIdentityToCookie(cookie);
+
+ cookie[WebInspector.ScriptTimelineRecord.EventTypeCookieKey] = this._eventType;
+ cookie[WebInspector.ScriptTimelineRecord.DetailsCookieKey] = this._details;
+ }
+
+ get profilePayload()
+ {
+ return this._profilePayload;
+ }
+
+ set profilePayload(payload)
+ {
+ this._profilePayload = payload;
+ }
+
+ // Private
+
+ _initializeProfileFromPayload(payload)
+ {
+ if (this._profile || !this._profilePayload)
+ return;
+
+ var payload = this._profilePayload;
+ this._profilePayload = undefined;
+
+ console.assert(payload.rootNodes instanceof Array);
+
+ function profileNodeFromPayload(nodePayload)
+ {
+ console.assert("id" in nodePayload);
+
+ if (nodePayload.url) {
+ var sourceCode = WebInspector.frameResourceManager.resourceForURL(nodePayload.url);
+ if (!sourceCode)
+ sourceCode = WebInspector.debuggerManager.scriptsForURL(nodePayload.url, WebInspector.assumingMainTarget())[0];
+
+ // The lineNumber is 1-based, but we expect 0-based.
+ var lineNumber = nodePayload.lineNumber - 1;
+
+ var sourceCodeLocation = sourceCode ? sourceCode.createLazySourceCodeLocation(lineNumber, nodePayload.columnNumber) : null;
+ }
+
+ var isProgramCode = nodePayload.functionName === "(program)";
+ var isAnonymousFunction = nodePayload.functionName === "(anonymous function)";
+
+ var type = isProgramCode ? WebInspector.ProfileNode.Type.Program : WebInspector.ProfileNode.Type.Function;
+ var functionName = !isProgramCode && !isAnonymousFunction && nodePayload.functionName !== "(unknown)" ? nodePayload.functionName : null;
+
+ // COMPATIBILITY (iOS 8): Timeline.CPUProfileNodes used to include an array of complete
+ // call information instead of the aggregated "callInfo" data.
+ var calls = null;
+ if ("calls" in nodePayload) {
+ console.assert(nodePayload.calls instanceof Array);
+ calls = nodePayload.calls.map(profileNodeCallFromPayload);
+ }
+
+ return new WebInspector.ProfileNode(nodePayload.id, type, functionName, sourceCodeLocation, nodePayload.callInfo, calls, nodePayload.children);
+ }
+
+ function profileNodeCallFromPayload(nodeCallPayload)
+ {
+ console.assert("startTime" in nodeCallPayload);
+ console.assert("totalTime" in nodeCallPayload);
+
+ var startTime = WebInspector.timelineManager.computeElapsedTime(nodeCallPayload.startTime);
+
+ return new WebInspector.ProfileNodeCall(startTime, nodeCallPayload.totalTime);
+ }
+
+ var rootNodes = payload.rootNodes;
+
+ // Iterate over the node tree using a stack. Doing this recursively can easily cause a stack overflow.
+ // We traverse the profile in post-order and convert the payloads in place until we get back to the root.
+ var stack = [{parent: {children: rootNodes}, index: 0, root: true}];
+ while (stack.length) {
+ var entry = stack.lastValue;
+
+ if (entry.index < entry.parent.children.length) {
+ var childNodePayload = entry.parent.children[entry.index];
+ if (childNodePayload.children && childNodePayload.children.length)
+ stack.push({parent: childNodePayload, index: 0});
+
+ ++entry.index;
+ } else {
+ if (!entry.root)
+ entry.parent.children = entry.parent.children.map(profileNodeFromPayload);
+ else
+ rootNodes = rootNodes.map(profileNodeFromPayload);
+
+ stack.pop();
+ }
+ }
+
+ // COMPATIBILITY (iOS 9): We only do this when we have ScriptProfilerAgent because before that we didn't have a Sampling Profiler.
+ if (window.ScriptProfilerAgent) {
+ for (let i = 0; i < rootNodes.length; i++)
+ this._callCountOrSamples += rootNodes[i].callInfo.callCount;
+ }
+
+ this._profile = new WebInspector.Profile(rootNodes);
+ }
+};
+
+WebInspector.ScriptTimelineRecord.EventType = {
+ ScriptEvaluated: "script-timeline-record-script-evaluated",
+ APIScriptEvaluated: "script-timeline-record-api-script-evaluated",
+ MicrotaskDispatched: "script-timeline-record-microtask-dispatched",
+ EventDispatched: "script-timeline-record-event-dispatched",
+ ProbeSampleRecorded: "script-timeline-record-probe-sample-recorded",
+ TimerFired: "script-timeline-record-timer-fired",
+ TimerInstalled: "script-timeline-record-timer-installed",
+ TimerRemoved: "script-timeline-record-timer-removed",
+ AnimationFrameFired: "script-timeline-record-animation-frame-fired",
+ AnimationFrameRequested: "script-timeline-record-animation-frame-requested",
+ AnimationFrameCanceled: "script-timeline-record-animation-frame-canceled",
+ ConsoleProfileRecorded: "script-timeline-record-console-profile-recorded",
+ GarbageCollected: "script-timeline-record-garbage-collected",
+};
+
+WebInspector.ScriptTimelineRecord.EventType.displayName = function(eventType, details, includeDetailsInMainTitle)
+{
+ if (details && !WebInspector.ScriptTimelineRecord._eventDisplayNames) {
+ // These display names are not localized because they closely represent
+ // the real API name, just with word spaces and Title Case.
+
+ var nameMap = new Map;
+ nameMap.set("DOMActivate", "DOM Activate");
+ nameMap.set("DOMCharacterDataModified", "DOM Character Data Modified");
+ nameMap.set("DOMContentLoaded", "DOM Content Loaded");
+ nameMap.set("DOMFocusIn", "DOM Focus In");
+ nameMap.set("DOMFocusOut", "DOM Focus Out");
+ nameMap.set("DOMNodeInserted", "DOM Node Inserted");
+ nameMap.set("DOMNodeInsertedIntoDocument", "DOM Node Inserted Into Document");
+ nameMap.set("DOMNodeRemoved", "DOM Node Removed");
+ nameMap.set("DOMNodeRemovedFromDocument", "DOM Node Removed From Document");
+ nameMap.set("DOMSubtreeModified", "DOM Sub-Tree Modified");
+ nameMap.set("addsourcebuffer", "Add Source Buffer");
+ nameMap.set("addstream", "Add Stream");
+ nameMap.set("addtrack", "Add Track");
+ nameMap.set("animationend", "Animation End");
+ nameMap.set("animationiteration", "Animation Iteration");
+ nameMap.set("animationstart", "Animation Start");
+ nameMap.set("audioend", "Audio End");
+ nameMap.set("audioprocess", "Audio Process");
+ nameMap.set("audiostart", "Audio Start");
+ nameMap.set("beforecopy", "Before Copy");
+ nameMap.set("beforecut", "Before Cut");
+ nameMap.set("beforeload", "Before Load");
+ nameMap.set("beforepaste", "Before Paste");
+ nameMap.set("beforeunload", "Before Unload");
+ nameMap.set("canplay", "Can Play");
+ nameMap.set("canplaythrough", "Can Play Through");
+ nameMap.set("chargingchange", "Charging Change");
+ nameMap.set("chargingtimechange", "Charging Time Change");
+ nameMap.set("compositionend", "Composition End");
+ nameMap.set("compositionstart", "Composition Start");
+ nameMap.set("compositionupdate", "Composition Update");
+ nameMap.set("contextmenu", "Context Menu");
+ nameMap.set("cuechange", "Cue Change");
+ nameMap.set("datachannel", "Data Channel");
+ nameMap.set("dblclick", "Double Click");
+ nameMap.set("devicemotion", "Device Motion");
+ nameMap.set("deviceorientation", "Device Orientation");
+ nameMap.set("dischargingtimechange", "Discharging Time Change");
+ nameMap.set("dragend", "Drag End");
+ nameMap.set("dragenter", "Drag Enter");
+ nameMap.set("dragleave", "Drag Leave");
+ nameMap.set("dragover", "Drag Over");
+ nameMap.set("dragstart", "Drag Start");
+ nameMap.set("durationchange", "Duration Change");
+ nameMap.set("focusin", "Focus In");
+ nameMap.set("focusout", "Focus Out");
+ nameMap.set("gesturechange", "Gesture Change");
+ nameMap.set("gestureend", "Gesture End");
+ nameMap.set("gesturescrollend", "Gesture Scroll End");
+ nameMap.set("gesturescrollstart", "Gesture Scroll Start");
+ nameMap.set("gesturescrollupdate", "Gesture Scroll Update");
+ nameMap.set("gesturestart", "Gesture Start");
+ nameMap.set("gesturetap", "Gesture Tap");
+ nameMap.set("gesturetapdown", "Gesture Tap Down");
+ nameMap.set("hashchange", "Hash Change");
+ nameMap.set("icecandidate", "ICE Candidate");
+ nameMap.set("iceconnectionstatechange", "ICE Connection State Change");
+ nameMap.set("keydown", "Key Down");
+ nameMap.set("keypress", "Key Press");
+ nameMap.set("keyup", "Key Up");
+ nameMap.set("levelchange", "Level Change");
+ nameMap.set("loadeddata", "Loaded Data");
+ nameMap.set("loadedmetadata", "Loaded Metadata");
+ nameMap.set("loadend", "Load End");
+ nameMap.set("loadingdone", "Loading Done");
+ nameMap.set("loadstart", "Load Start");
+ nameMap.set("mousedown", "Mouse Down");
+ nameMap.set("mouseenter", "Mouse Enter");
+ nameMap.set("mouseleave", "Mouse Leave");
+ nameMap.set("mousemove", "Mouse Move");
+ nameMap.set("mouseout", "Mouse Out");
+ nameMap.set("mouseover", "Mouse Over");
+ nameMap.set("mouseup", "Mouse Up");
+ nameMap.set("mousewheel", "Mouse Wheel");
+ nameMap.set("negotiationneeded", "Negotiation Needed");
+ nameMap.set("nomatch", "No Match");
+ nameMap.set("noupdate", "No Update");
+ nameMap.set("orientationchange", "Orientation Change");
+ nameMap.set("overflowchanged", "Overflow Changed");
+ nameMap.set("pagehide", "Page Hide");
+ nameMap.set("pageshow", "Page Show");
+ nameMap.set("popstate", "Pop State");
+ nameMap.set("ratechange", "Rate Change");
+ nameMap.set("readystatechange", "Ready State Change");
+ nameMap.set("removesourcebuffer", "Remove Source Buffer");
+ nameMap.set("removestream", "Remove Stream");
+ nameMap.set("removetrack", "Remove Track");
+ nameMap.set("resize", "Resize");
+ nameMap.set("securitypolicyviolation", "Security Policy Violation");
+ nameMap.set("selectionchange", "Selection Change");
+ nameMap.set("selectstart", "Select Start");
+ nameMap.set("signalingstatechange", "Signaling State Change");
+ nameMap.set("soundend", "Sound End");
+ nameMap.set("soundstart", "Sound Start");
+ nameMap.set("sourceclose", "Source Close");
+ nameMap.set("sourceended", "Source Ended");
+ nameMap.set("sourceopen", "Source Open");
+ nameMap.set("speechend", "Speech End");
+ nameMap.set("speechstart", "Speech Start");
+ nameMap.set("textInput", "Text Input");
+ nameMap.set("timeupdate", "Time Update");
+ nameMap.set("tonechange", "Tone Change");
+ nameMap.set("touchcancel", "Touch Cancel");
+ nameMap.set("touchend", "Touch End");
+ nameMap.set("touchmove", "Touch Move");
+ nameMap.set("touchstart", "Touch Start");
+ nameMap.set("transitionend", "Transition End");
+ nameMap.set("updateend", "Update End");
+ nameMap.set("updateready", "Update Ready");
+ nameMap.set("updatestart", "Update Start");
+ nameMap.set("upgradeneeded", "Upgrade Needed");
+ nameMap.set("versionchange", "Version Change");
+ nameMap.set("visibilitychange", "Visibility Change");
+ nameMap.set("volumechange", "Volume Change");
+ nameMap.set("webglcontextcreationerror", "WebGL Context Creation Error");
+ nameMap.set("webglcontextlost", "WebGL Context Lost");
+ nameMap.set("webglcontextrestored", "WebGL Context Restored");
+ nameMap.set("webkitAnimationEnd", "Animation End");
+ nameMap.set("webkitAnimationIteration", "Animation Iteration");
+ nameMap.set("webkitAnimationStart", "Animation Start");
+ nameMap.set("webkitBeforeTextInserted", "Before Text Inserted");
+ nameMap.set("webkitEditableContentChanged", "Editable Content Changed");
+ nameMap.set("webkitTransitionEnd", "Transition End");
+ nameMap.set("webkitaddsourcebuffer", "Add Source Buffer");
+ nameMap.set("webkitbeginfullscreen", "Begin Fullscreen");
+ nameMap.set("webkitcurrentplaybacktargetiswirelesschanged", "Current Playback Target Is Wireless Changed");
+ nameMap.set("webkitdeviceproximity", "Device Proximity");
+ nameMap.set("webkitendfullscreen", "End Fullscreen");
+ nameMap.set("webkitfullscreenchange", "Fullscreen Change");
+ nameMap.set("webkitfullscreenerror", "Fullscreen Error");
+ nameMap.set("webkitkeyadded", "Key Added");
+ nameMap.set("webkitkeyerror", "Key Error");
+ nameMap.set("webkitkeymessage", "Key Message");
+ nameMap.set("webkitneedkey", "Need Key");
+ nameMap.set("webkitnetworkinfochange", "Network Info Change");
+ nameMap.set("webkitplaybacktargetavailabilitychanged", "Playback Target Availability Changed");
+ nameMap.set("webkitpointerlockchange", "Pointer Lock Change");
+ nameMap.set("webkitpointerlockerror", "Pointer Lock Error");
+ nameMap.set("webkitregionlayoutupdate", "Region Layout Update"); // COMPATIBILITY (iOS 7): regionLayoutUpdated was removed and replaced by regionOversetChanged.
+ nameMap.set("webkitregionoversetchange", "Region Overset Change");
+ nameMap.set("webkitremovesourcebuffer", "Remove Source Buffer");
+ nameMap.set("webkitresourcetimingbufferfull", "Resource Timing Buffer Full");
+ nameMap.set("webkitsourceclose", "Source Close");
+ nameMap.set("webkitsourceended", "Source Ended");
+ nameMap.set("webkitsourceopen", "Source Open");
+ nameMap.set("webkitspeechchange", "Speech Change");
+ nameMap.set("writeend", "Write End");
+ nameMap.set("writestart", "Write Start");
+
+ WebInspector.ScriptTimelineRecord._eventDisplayNames = nameMap;
+ }
+
+ switch (eventType) {
+ case WebInspector.ScriptTimelineRecord.EventType.ScriptEvaluated:
+ case WebInspector.ScriptTimelineRecord.EventType.APIScriptEvaluated:
+ return WebInspector.UIString("Script Evaluated");
+ case WebInspector.ScriptTimelineRecord.EventType.MicrotaskDispatched:
+ return WebInspector.UIString("Microtask Dispatched");
+ case WebInspector.ScriptTimelineRecord.EventType.EventDispatched:
+ if (details && (details instanceof String || typeof details === "string")) {
+ var eventDisplayName = WebInspector.ScriptTimelineRecord._eventDisplayNames.get(details) || details.capitalize();
+ return WebInspector.UIString("%s Event Dispatched").format(eventDisplayName);
+ }
+ return WebInspector.UIString("Event Dispatched");
+ case WebInspector.ScriptTimelineRecord.EventType.ProbeSampleRecorded:
+ return WebInspector.UIString("Probe Sample Recorded");
+ case WebInspector.ScriptTimelineRecord.EventType.ConsoleProfileRecorded:
+ if (details && (details instanceof String || typeof details === "string"))
+ return WebInspector.UIString("“%s” Profile Recorded").format(details);
+ return WebInspector.UIString("Console Profile Recorded");
+ case WebInspector.ScriptTimelineRecord.EventType.GarbageCollected:
+ console.assert(details);
+ if (details && (details instanceof WebInspector.GarbageCollection) && includeDetailsInMainTitle) {
+ switch (details.type) {
+ case WebInspector.GarbageCollection.Type.Partial:
+ return WebInspector.UIString("Partial Garbage Collection");
+ case WebInspector.GarbageCollection.Type.Full:
+ return WebInspector.UIString("Full Garbage Collection");
+ }
+ }
+ return WebInspector.UIString("Garbage Collection");
+ case WebInspector.ScriptTimelineRecord.EventType.TimerFired:
+ if (details && includeDetailsInMainTitle)
+ return WebInspector.UIString("Timer %s Fired").format(details);
+ return WebInspector.UIString("Timer Fired");
+ case WebInspector.ScriptTimelineRecord.EventType.TimerInstalled:
+ if (details && includeDetailsInMainTitle)
+ return WebInspector.UIString("Timer %s Installed").format(details.timerId);
+ return WebInspector.UIString("Timer Installed");
+ case WebInspector.ScriptTimelineRecord.EventType.TimerRemoved:
+ if (details && includeDetailsInMainTitle)
+ return WebInspector.UIString("Timer %s Removed").format(details);
+ return WebInspector.UIString("Timer Removed");
+ case WebInspector.ScriptTimelineRecord.EventType.AnimationFrameFired:
+ if (details && includeDetailsInMainTitle)
+ return WebInspector.UIString("Animation Frame %s Fired").format(details);
+ return WebInspector.UIString("Animation Frame Fired");
+ case WebInspector.ScriptTimelineRecord.EventType.AnimationFrameRequested:
+ if (details && includeDetailsInMainTitle)
+ return WebInspector.UIString("Animation Frame %s Requested").format(details);
+ return WebInspector.UIString("Animation Frame Requested");
+ case WebInspector.ScriptTimelineRecord.EventType.AnimationFrameCanceled:
+ if (details && includeDetailsInMainTitle)
+ return WebInspector.UIString("Animation Frame %s Canceled").format(details);
+ return WebInspector.UIString("Animation Frame Canceled");
+ }
+};
+
+WebInspector.ScriptTimelineRecord.TypeIdentifier = "script-timeline-record";
+WebInspector.ScriptTimelineRecord.EventTypeCookieKey = "script-timeline-record-event-type";
+WebInspector.ScriptTimelineRecord.DetailsCookieKey = "script-timeline-record-details";
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCode.js b/Source/WebInspectorUI/UserInterface/Models/SourceCode.js
new file mode 100644
index 000000000..29e8f3231
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCode.js
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCode = class SourceCode extends WebInspector.Object
+{
+ constructor()
+ {
+ super();
+
+ this._originalRevision = new WebInspector.SourceCodeRevision(this, null, false);
+ this._currentRevision = this._originalRevision;
+
+ this._sourceMaps = null;
+ this._formatterSourceMap = null;
+ this._requestContentPromise = null;
+ }
+
+ // Public
+
+ get displayName()
+ {
+ // Implemented by subclasses.
+ console.error("Needs to be implemented by a subclass.");
+ return "";
+ }
+
+ get originalRevision()
+ {
+ return this._originalRevision;
+ }
+
+ get currentRevision()
+ {
+ return this._currentRevision;
+ }
+
+ set currentRevision(revision)
+ {
+ console.assert(revision instanceof WebInspector.SourceCodeRevision);
+ if (!(revision instanceof WebInspector.SourceCodeRevision))
+ return;
+
+ console.assert(revision.sourceCode === this);
+ if (revision.sourceCode !== this)
+ return;
+
+ this._currentRevision = revision;
+
+ this.dispatchEventToListeners(WebInspector.SourceCode.Event.ContentDidChange);
+ }
+
+ get content()
+ {
+ return this._currentRevision.content;
+ }
+
+ get url()
+ {
+ // To be overridden by subclasses.
+ }
+
+ get contentIdentifier()
+ {
+ // A contentIdentifier is roughly `url || sourceURL` for cases where
+ // the content is consistent between sessions and not ephemeral.
+
+ // Can be overridden by subclasses if better behavior is possible.
+ return this.url;
+ }
+
+ get sourceMaps()
+ {
+ return this._sourceMaps || [];
+ }
+
+ addSourceMap(sourceMap)
+ {
+ console.assert(sourceMap instanceof WebInspector.SourceMap);
+
+ if (!this._sourceMaps)
+ this._sourceMaps = [];
+
+ this._sourceMaps.push(sourceMap);
+
+ this.dispatchEventToListeners(WebInspector.SourceCode.Event.SourceMapAdded);
+ }
+
+ get formatterSourceMap()
+ {
+ return this._formatterSourceMap;
+ }
+
+ set formatterSourceMap(formatterSourceMap)
+ {
+ console.assert(this._formatterSourceMap === null || formatterSourceMap === null);
+ console.assert(formatterSourceMap === null || formatterSourceMap instanceof WebInspector.FormatterSourceMap);
+
+ this._formatterSourceMap = formatterSourceMap;
+
+ this.dispatchEventToListeners(WebInspector.SourceCode.Event.FormatterDidChange);
+ }
+
+ requestContent()
+ {
+ this._requestContentPromise = this._requestContentPromise || this.requestContentFromBackend().then(this._processContent.bind(this));
+
+ return this._requestContentPromise;
+ }
+
+ createSourceCodeLocation(lineNumber, columnNumber)
+ {
+ return new WebInspector.SourceCodeLocation(this, lineNumber, columnNumber);
+ }
+
+ createLazySourceCodeLocation(lineNumber, columnNumber)
+ {
+ return new WebInspector.LazySourceCodeLocation(this, lineNumber, columnNumber);
+ }
+
+ createSourceCodeTextRange(textRange)
+ {
+ return new WebInspector.SourceCodeTextRange(this, textRange);
+ }
+
+ // Protected
+
+ revisionContentDidChange(revision)
+ {
+ if (this._ignoreRevisionContentDidChangeEvent)
+ return;
+
+ if (revision !== this._currentRevision)
+ return;
+
+ this.handleCurrentRevisionContentChange();
+
+ this.dispatchEventToListeners(WebInspector.SourceCode.Event.ContentDidChange);
+ }
+
+ handleCurrentRevisionContentChange()
+ {
+ // Implemented by subclasses if needed.
+ }
+
+ get revisionForRequestedContent()
+ {
+ // Implemented by subclasses if needed.
+ return this._originalRevision;
+ }
+
+ markContentAsStale()
+ {
+ this._requestContentPromise = null;
+ this._contentReceived = false;
+ }
+
+ requestContentFromBackend()
+ {
+ // Implemented by subclasses.
+ console.error("Needs to be implemented by a subclass.");
+ return Promise.reject(new Error("Needs to be implemented by a subclass."));
+ }
+
+ get mimeType()
+ {
+ // Implemented by subclasses.
+ console.error("Needs to be implemented by a subclass.");
+ }
+
+ // Private
+
+ _processContent(parameters)
+ {
+ // Different backend APIs return one of `content, `body`, `text`, or `scriptSource`.
+ var content = parameters.content || parameters.body || parameters.text || parameters.scriptSource;
+ var error = parameters.error;
+ if (parameters.base64Encoded)
+ content = decodeBase64ToBlob(content, this.mimeType);
+
+ var revision = this.revisionForRequestedContent;
+
+ this._ignoreRevisionContentDidChangeEvent = true;
+ revision.content = content || null;
+ this._ignoreRevisionContentDidChangeEvent = false;
+
+ // FIXME: Returning the content in this promise is misleading. It may not be current content
+ // now, and it may become out-dated later on. We should drop content from this promise
+ // and require clients to ask for the current contents from the sourceCode in the result.
+
+ return Promise.resolve({
+ error,
+ sourceCode: this,
+ content,
+ });
+ }
+};
+
+WebInspector.SourceCode.Event = {
+ ContentDidChange: "source-code-content-did-change",
+ SourceMapAdded: "source-code-source-map-added",
+ FormatterDidChange: "source-code-formatter-did-change",
+ LoadingDidFinish: "source-code-loading-did-finish",
+ LoadingDidFail: "source-code-loading-did-fail"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js
new file mode 100644
index 000000000..79691700c
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCodeLocation = class SourceCodeLocation extends WebInspector.Object
+{
+ constructor(sourceCode, lineNumber, columnNumber)
+ {
+ super();
+
+ console.assert(sourceCode === null || sourceCode instanceof WebInspector.SourceCode);
+ console.assert(!(sourceCode instanceof WebInspector.SourceMapResource));
+ console.assert(typeof lineNumber === "number" && !isNaN(lineNumber) && lineNumber >= 0);
+ console.assert(typeof columnNumber === "number" && !isNaN(columnNumber) && columnNumber >= 0);
+
+ this._sourceCode = sourceCode || null;
+ this._lineNumber = lineNumber;
+ this._columnNumber = columnNumber;
+ this._resolveFormattedLocation();
+
+ if (this._sourceCode) {
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.FormatterDidChange, this._sourceCodeFormatterDidChange, this);
+ }
+
+ this._resetMappedLocation();
+ }
+
+ // Public
+
+ isEqual(other)
+ {
+ if (!other)
+ return false;
+ return this._sourceCode === other._sourceCode && this._lineNumber === other._lineNumber && this._columnNumber === other._columnNumber;
+ }
+
+ get sourceCode()
+ {
+ return this._sourceCode;
+ }
+
+ set sourceCode(sourceCode)
+ {
+ this.setSourceCode(sourceCode);
+ }
+
+ // Raw line and column in the original source code.
+
+ get lineNumber()
+ {
+ return this._lineNumber;
+ }
+
+ get columnNumber()
+ {
+ return this._columnNumber;
+ }
+
+ position()
+ {
+ return new WebInspector.SourceCodePosition(this.lineNumber, this.columnNumber);
+ }
+
+ // Formatted line and column if the original source code is pretty printed.
+ // This is the same as the raw location if there is no formatter.
+
+ get formattedLineNumber()
+ {
+ return this._formattedLineNumber;
+ }
+
+ get formattedColumnNumber()
+ {
+ return this._formattedColumnNumber;
+ }
+
+ formattedPosition()
+ {
+ return new WebInspector.SourceCodePosition(this.formattedLineNumber, this.formattedColumnNumber);
+ }
+
+ // Display line and column:
+ // - Mapped line and column if the original source code has a source map.
+ // - Otherwise this is the formatted / raw line and column.
+
+ get displaySourceCode()
+ {
+ this.resolveMappedLocation();
+ return this._mappedResource || this._sourceCode;
+ }
+
+ get displayLineNumber()
+ {
+ this.resolveMappedLocation();
+ return isNaN(this._mappedLineNumber) ? this._formattedLineNumber : this._mappedLineNumber;
+ }
+
+ get displayColumnNumber()
+ {
+ this.resolveMappedLocation();
+ return isNaN(this._mappedColumnNumber) ? this._formattedColumnNumber : this._mappedColumnNumber;
+ }
+
+ displayPosition()
+ {
+ return new WebInspector.SourceCodePosition(this.displayLineNumber, this.displayColumnNumber);
+ }
+
+ // User presentable location strings: "file:lineNumber:columnNumber".
+
+ originalLocationString(columnStyle, nameStyle, prefix)
+ {
+ return this._locationString(this.sourceCode, this.lineNumber, this.columnNumber, columnStyle, nameStyle, prefix);
+ }
+
+ formattedLocationString(columnStyle, nameStyle, prefix)
+ {
+ return this._locationString(this.sourceCode, this.formattedLineNumber, this.formattedColumn, columnStyle, nameStyle, prefix);
+ }
+
+ displayLocationString(columnStyle, nameStyle, prefix)
+ {
+ return this._locationString(this.displaySourceCode, this.displayLineNumber, this.displayColumnNumber, columnStyle, nameStyle, prefix);
+ }
+
+ tooltipString()
+ {
+ if (!this.hasDifferentDisplayLocation())
+ return this.originalLocationString(WebInspector.SourceCodeLocation.ColumnStyle.Shown, WebInspector.SourceCodeLocation.NameStyle.Full);
+
+ var tooltip = WebInspector.UIString("Located at %s").format(this.displayLocationString(WebInspector.SourceCodeLocation.ColumnStyle.Shown, WebInspector.SourceCodeLocation.NameStyle.Full));
+ tooltip += "\n" + WebInspector.UIString("Originally %s").format(this.originalLocationString(WebInspector.SourceCodeLocation.ColumnStyle.Shown, WebInspector.SourceCodeLocation.NameStyle.Full));
+ return tooltip;
+ }
+
+ hasMappedLocation()
+ {
+ this.resolveMappedLocation();
+ return this._mappedResource !== null;
+ }
+
+ hasFormattedLocation()
+ {
+ return this._formattedLineNumber !== this._lineNumber || this._formattedColumnNumber !== this._columnNumber;
+ }
+
+ hasDifferentDisplayLocation()
+ {
+ return this.hasMappedLocation() || this.hasFormattedLocation();
+ }
+
+ update(sourceCode, lineNumber, columnNumber)
+ {
+ console.assert(sourceCode === this._sourceCode || (this._mappedResource && sourceCode === this._mappedResource));
+ console.assert(typeof lineNumber === "number" && !isNaN(lineNumber) && lineNumber >= 0);
+ console.assert(typeof columnNumber === "number" && !isNaN(columnNumber) && columnNumber >= 0);
+
+ if (sourceCode === this._sourceCode && lineNumber === this._lineNumber && columnNumber === this._columnNumber)
+ return;
+ if (this._mappedResource && sourceCode === this._mappedResource && lineNumber === this._mappedLineNumber && columnNumber === this._mappedColumnNumber)
+ return;
+
+ var newSourceCodeLocation = sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
+ console.assert(newSourceCodeLocation.sourceCode === this._sourceCode);
+
+ this._makeChangeAndDispatchChangeEventIfNeeded(function() {
+ this._lineNumber = newSourceCodeLocation._lineNumber;
+ this._columnNumber = newSourceCodeLocation._columnNumber;
+ if (newSourceCodeLocation._mappedLocationIsResolved) {
+ this._mappedLocationIsResolved = true;
+ this._mappedResource = newSourceCodeLocation._mappedResource;
+ this._mappedLineNumber = newSourceCodeLocation._mappedLineNumber;
+ this._mappedColumnNumber = newSourceCodeLocation._mappedColumnNumber;
+ }
+ });
+ }
+
+ populateLiveDisplayLocationTooltip(element, prefix)
+ {
+ prefix = prefix || "";
+
+ element.title = prefix + this.tooltipString();
+
+ this.addEventListener(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, function(event) {
+ if (this.sourceCode)
+ element.title = prefix + this.tooltipString();
+ }, this);
+ }
+
+ populateLiveDisplayLocationString(element, propertyName, columnStyle, nameStyle, prefix)
+ {
+ var currentDisplay;
+
+ function updateDisplayString(showAlternativeLocation, forceUpdate)
+ {
+ if (!forceUpdate && currentDisplay === showAlternativeLocation)
+ return;
+
+ currentDisplay = showAlternativeLocation;
+
+ if (!showAlternativeLocation) {
+ element[propertyName] = this.displayLocationString(columnStyle, nameStyle, prefix);
+ element.classList.toggle(WebInspector.SourceCodeLocation.DisplayLocationClassName, this.hasDifferentDisplayLocation());
+ } else if (this.hasDifferentDisplayLocation()) {
+ element[propertyName] = this.originalLocationString(columnStyle, nameStyle, prefix);
+ element.classList.remove(WebInspector.SourceCodeLocation.DisplayLocationClassName);
+ }
+ }
+
+ function mouseOverOrMove(event)
+ {
+ updateDisplayString.call(this, event.metaKey && !event.altKey && !event.shiftKey);
+ }
+
+ updateDisplayString.call(this, false);
+
+ this.addEventListener(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, function(event) {
+ if (this.sourceCode)
+ updateDisplayString.call(this, currentDisplay, true);
+ }, this);
+
+ var boundMouseOverOrMove = mouseOverOrMove.bind(this);
+ element.addEventListener("mouseover", boundMouseOverOrMove);
+ element.addEventListener("mousemove", boundMouseOverOrMove);
+ element.addEventListener("mouseout", (event) => { updateDisplayString.call(this, false); });
+ }
+
+ // Protected
+
+ setSourceCode(sourceCode)
+ {
+ console.assert((this._sourceCode === null && sourceCode instanceof WebInspector.SourceCode) || (this._sourceCode instanceof WebInspector.SourceCode && sourceCode === null));
+
+ if (sourceCode === this._sourceCode)
+ return;
+
+ this._makeChangeAndDispatchChangeEventIfNeeded(function() {
+ if (this._sourceCode) {
+ this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+ this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.FormatterDidChange, this._sourceCodeFormatterDidChange, this);
+ }
+
+ this._sourceCode = sourceCode;
+
+ if (this._sourceCode) {
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.FormatterDidChange, this._sourceCodeFormatterDidChange, this);
+ }
+ });
+ }
+
+ resolveMappedLocation()
+ {
+ if (this._mappedLocationIsResolved)
+ return;
+
+ console.assert(this._mappedResource === null);
+ console.assert(isNaN(this._mappedLineNumber));
+ console.assert(isNaN(this._mappedColumnNumber));
+
+ this._mappedLocationIsResolved = true;
+
+ if (!this._sourceCode)
+ return;
+
+ var sourceMaps = this._sourceCode.sourceMaps;
+ if (!sourceMaps.length)
+ return;
+
+ for (var i = 0; i < sourceMaps.length; ++i) {
+ var sourceMap = sourceMaps[i];
+ var entry = sourceMap.findEntry(this._lineNumber, this._columnNumber);
+ if (!entry || entry.length === 2)
+ continue;
+ console.assert(entry.length === 5);
+ var url = entry[2];
+ var sourceMapResource = sourceMap.resourceForURL(url);
+ if (!sourceMapResource)
+ return;
+ this._mappedResource = sourceMapResource;
+ this._mappedLineNumber = entry[3];
+ this._mappedColumnNumber = entry[4];
+ return;
+ }
+ }
+
+ // Private
+
+ _locationString(sourceCode, lineNumber, columnNumber, columnStyle, nameStyle, prefix)
+ {
+ console.assert(sourceCode);
+ if (!sourceCode)
+ return "";
+
+ columnStyle = columnStyle || WebInspector.SourceCodeLocation.ColumnStyle.OnlyIfLarge;
+ nameStyle = nameStyle || WebInspector.SourceCodeLocation.NameStyle.Short;
+ prefix = prefix || "";
+
+ let lineString = lineNumber + 1; // The user visible line number is 1-based.
+ if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.Shown && columnNumber > 0)
+ lineString += ":" + (columnNumber + 1); // The user visible column number is 1-based.
+ else if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.OnlyIfLarge && columnNumber > WebInspector.SourceCodeLocation.LargeColumnNumber)
+ lineString += ":" + (columnNumber + 1); // The user visible column number is 1-based.
+ else if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.Hidden)
+ lineString = "";
+
+ switch (nameStyle) {
+ case WebInspector.SourceCodeLocation.NameStyle.None:
+ return prefix + lineString;
+
+ case WebInspector.SourceCodeLocation.NameStyle.Short:
+ case WebInspector.SourceCodeLocation.NameStyle.Full:
+ var displayURL = sourceCode.displayURL;
+ var name = nameStyle === WebInspector.SourceCodeLocation.NameStyle.Full && displayURL ? displayURL : sourceCode.displayName;
+ if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.Hidden)
+ return prefix + name;
+ var lineSuffix = displayURL ? ":" + lineString : WebInspector.UIString(" (line %s)").format(lineString);
+ return prefix + name + lineSuffix;
+
+ default:
+ console.error("Unknown nameStyle: " + nameStyle);
+ return prefix + lineString;
+ }
+ }
+
+ _resetMappedLocation()
+ {
+ this._mappedLocationIsResolved = false;
+ this._mappedResource = null;
+ this._mappedLineNumber = NaN;
+ this._mappedColumnNumber = NaN;
+ }
+
+ _setMappedLocation(mappedResource, mappedLineNumber, mappedColumnNumber)
+ {
+ // Called by SourceMapResource when it creates a SourceCodeLocation and already knows the resolved location.
+ this._mappedLocationIsResolved = true;
+ this._mappedResource = mappedResource;
+ this._mappedLineNumber = mappedLineNumber;
+ this._mappedColumnNumber = mappedColumnNumber;
+ }
+
+ _resolveFormattedLocation()
+ {
+ if (this._sourceCode && this._sourceCode.formatterSourceMap) {
+ var formattedLocation = this._sourceCode.formatterSourceMap.originalToFormatted(this._lineNumber, this._columnNumber);
+ this._formattedLineNumber = formattedLocation.lineNumber;
+ this._formattedColumnNumber = formattedLocation.columnNumber;
+ } else {
+ this._formattedLineNumber = this._lineNumber;
+ this._formattedColumnNumber = this._columnNumber;
+ }
+ }
+
+ _makeChangeAndDispatchChangeEventIfNeeded(changeFunction)
+ {
+ var oldSourceCode = this._sourceCode;
+ var oldLineNumber = this._lineNumber;
+ var oldColumnNumber = this._columnNumber;
+
+ var oldFormattedLineNumber = this._formattedLineNumber;
+ var oldFormattedColumnNumber = this._formattedColumnNumber;
+
+ var oldDisplaySourceCode = this.displaySourceCode;
+ var oldDisplayLineNumber = this.displayLineNumber;
+ var oldDisplayColumnNumber = this.displayColumnNumber;
+
+ this._resetMappedLocation();
+
+ if (changeFunction)
+ changeFunction.call(this);
+
+ this.resolveMappedLocation();
+ this._resolveFormattedLocation();
+
+ // If the display source code is non-null then the addresses are not NaN and can be compared.
+ var displayLocationChanged = false;
+ var newDisplaySourceCode = this.displaySourceCode;
+ if (oldDisplaySourceCode !== newDisplaySourceCode)
+ displayLocationChanged = true;
+ else if (newDisplaySourceCode && (oldDisplayLineNumber !== this.displayLineNumber || oldDisplayColumnNumber !== this.displayColumnNumber))
+ displayLocationChanged = true;
+
+ var anyLocationChanged = false;
+ if (displayLocationChanged)
+ anyLocationChanged = true;
+ else if (oldSourceCode !== this._sourceCode)
+ anyLocationChanged = true;
+ else if (this._sourceCode && (oldLineNumber !== this._lineNumber || oldColumnNumber !== this._columnNumber))
+ anyLocationChanged = true;
+ else if (this._sourceCode && (oldFormattedLineNumber !== this._formattedLineNumber || oldFormattedColumnNumber !== this._formattedColumnNumber))
+ anyLocationChanged = true;
+
+ if (displayLocationChanged || anyLocationChanged) {
+ var oldData = {
+ oldSourceCode,
+ oldLineNumber,
+ oldColumnNumber,
+ oldFormattedLineNumber,
+ oldFormattedColumnNumber,
+ oldDisplaySourceCode,
+ oldDisplayLineNumber,
+ oldDisplayColumnNumber
+ };
+ if (displayLocationChanged)
+ this.dispatchEventToListeners(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, oldData);
+ if (anyLocationChanged)
+ this.dispatchEventToListeners(WebInspector.SourceCodeLocation.Event.LocationChanged, oldData);
+ }
+ }
+
+ _sourceCodeSourceMapAdded()
+ {
+ this._makeChangeAndDispatchChangeEventIfNeeded(null);
+ }
+
+ _sourceCodeFormatterDidChange()
+ {
+ this._makeChangeAndDispatchChangeEventIfNeeded(null);
+ }
+};
+
+WebInspector.SourceCodeLocation.DisplayLocationClassName = "display-location";
+
+WebInspector.SourceCodeLocation.LargeColumnNumber = 80;
+
+WebInspector.SourceCodeLocation.NameStyle = {
+ None: "none", // File name not included.
+ Short: "short", // Only the file name.
+ Full: "full" // Full URL is used.
+};
+
+WebInspector.SourceCodeLocation.ColumnStyle = {
+ Hidden: "hidden", // line and column numbers are not included.
+ OnlyIfLarge: "only-if-large", // column numbers greater than 80 are shown.
+ Shown: "shown" // non-zero column numbers are shown.
+};
+
+WebInspector.SourceCodeLocation.Event = {
+ LocationChanged: "source-code-location-location-changed",
+ DisplayLocationChanged: "source-code-location-display-location-changed"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodePosition.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodePosition.js
new file mode 100644
index 000000000..a89554ac2
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodePosition.js
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCodePosition = class SourceCodePosition extends WebInspector.Object
+{
+ constructor(lineNumber, columNumber)
+ {
+ super();
+
+ this._lineNumber = lineNumber || 0;
+ this._columnNumber = columNumber || 0;
+ }
+
+ // Public
+
+ get lineNumber() { return this._lineNumber; }
+ get columnNumber() { return this._columnNumber; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js
new file mode 100644
index 000000000..b955493ad
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCodeRevision = class SourceCodeRevision extends WebInspector.Revision
+{
+ constructor(sourceCode, content)
+ {
+ super();
+
+ console.assert(sourceCode instanceof WebInspector.SourceCode);
+
+ this._sourceCode = sourceCode;
+ this._content = content || "";
+ }
+
+ // Public
+
+ get sourceCode()
+ {
+ return this._sourceCode;
+ }
+
+ get content()
+ {
+ return this._content;
+ }
+
+ set content(content)
+ {
+ content = content || "";
+
+ if (this._content === content)
+ return;
+
+ this._content = content;
+
+ this._sourceCode.revisionContentDidChange(this);
+ }
+
+ apply()
+ {
+ this._sourceCode.currentRevision = this;
+ }
+
+ revert()
+ {
+ this._sourceCode.currentRevision = this._sourceCode.originalRevision;
+ }
+
+ copy()
+ {
+ return new WebInspector.SourceCodeRevision(this._sourceCode, this._content);
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodeSearchMatchObject.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodeSearchMatchObject.js
new file mode 100644
index 000000000..7bab2d6ec
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodeSearchMatchObject.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCodeSearchMatchObject = class SourceCodeSearchMatchObject extends WebInspector.Object
+{
+ constructor(sourceCode, lineText, searchTerm, textRange)
+ {
+ super();
+
+ console.assert(sourceCode instanceof WebInspector.SourceCode);
+
+ this._sourceCode = sourceCode;
+ this._lineText = lineText;
+ this._searchTerm = searchTerm;
+ this._sourceCodeTextRange = sourceCode.createSourceCodeTextRange(textRange);
+ }
+
+ // Public
+
+ get sourceCode() { return this._sourceCode; }
+ get title() { return this._lineText; }
+ get searchTerm() { return this._searchTerm; }
+ get sourceCodeTextRange() { return this._sourceCodeTextRange; }
+
+ get className()
+ {
+ return WebInspector.SourceCodeSearchMatchObject.SourceCodeMatchIconStyleClassName;
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ if (this._sourceCode.url)
+ cookie[WebInspector.SourceCodeSearchMatchObject.URLCookieKey] = this._sourceCode.url.hash;
+
+ var textRange = this._sourceCodeTextRange.textRange;
+ cookie[WebInspector.SourceCodeSearchMatchObject.TextRangeKey] = [textRange.startLine, textRange.startColumn, textRange.endLine, textRange.endColumn].join();
+ }
+};
+
+WebInspector.SourceCodeSearchMatchObject.SourceCodeMatchIconStyleClassName = "source-code-match-icon";
+
+WebInspector.SourceCodeSearchMatchObject.TypeIdentifier = "source-code-search-match-object";
+WebInspector.SourceCodeSearchMatchObject.URLCookieKey = "source-code-url";
+WebInspector.SourceCodeSearchMatchObject.TextRangeKey = "text-range";
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodeTextRange.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodeTextRange.js
new file mode 100644
index 000000000..216743249
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodeTextRange.js
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCodeTextRange = class SourceCodeTextRange extends WebInspector.Object
+{
+ constructor(sourceCode) /* textRange || startLocation, endLocation */
+ {
+ super();
+
+ console.assert(sourceCode instanceof WebInspector.SourceCode);
+ console.assert(arguments.length === 2 || arguments.length === 3);
+
+ this._sourceCode = sourceCode;
+
+ if (arguments.length === 2) {
+ var textRange = arguments[1];
+ console.assert(textRange instanceof WebInspector.TextRange);
+ this._startLocation = sourceCode.createSourceCodeLocation(textRange.startLine, textRange.startColumn);
+ this._endLocation = sourceCode.createSourceCodeLocation(textRange.endLine, textRange.endColumn);
+ } else {
+ console.assert(arguments[1] instanceof WebInspector.SourceCodeLocation);
+ console.assert(arguments[2] instanceof WebInspector.SourceCodeLocation);
+ this._startLocation = arguments[1];
+ this._endLocation = arguments[2];
+ }
+
+ this._startLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._sourceCodeLocationChanged, this);
+ this._endLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._sourceCodeLocationChanged, this);
+ }
+
+ // Public
+
+ get sourceCode()
+ {
+ return this._sourceCode;
+ }
+
+ // Raw text range in the original source code.
+
+ get textRange()
+ {
+ var startLine = this._startLocation.lineNumber;
+ var startColumn = this._startLocation.columnNumber;
+ var endLine = this._endLocation.lineNumber;
+ var endColumn = this._endLocation.columnNumber;
+ return new WebInspector.TextRange(startLine, startColumn, endLine, endColumn);
+ }
+
+ // Formatted text range in the original source code if it is pretty printed.
+ // This is the same as the raw text range if the source code has no formatter.
+
+ get formattedTextRange()
+ {
+ var startLine = this._startLocation.formattedLineNumber;
+ var startColumn = this._startLocation.formattedColumnNumber;
+ var endLine = this._endLocation.formattedLineNumber;
+ var endColumn = this._endLocation.formattedColumnNumber;
+ return new WebInspector.TextRange(startLine, startColumn, endLine, endColumn);
+ }
+
+ // Display values:
+ // - Mapped resource and text range locations if the original source code has a
+ // source map and both start and end locations are in the same mapped resource.
+ // - Otherwise this is the formatted / raw text range.
+
+ get displaySourceCode()
+ {
+ if (!this._startAndEndLocationsInSameMappedResource())
+ return this._sourceCode;
+
+ return this._startLocation.displaySourceCode;
+ }
+
+ get displayTextRange()
+ {
+ if (!this._startAndEndLocationsInSameMappedResource())
+ return this.formattedTextRange;
+
+ var startLine = this._startLocation.displayLineNumber;
+ var startColumn = this._startLocation.displayColumnNumber;
+ var endLine = this._endLocation.displayLineNumber;
+ var endColumn = this._endLocation.displayColumnNumber;
+ return new WebInspector.TextRange(startLine, startColumn, endLine, endColumn);
+ }
+
+ // Private
+
+ _startAndEndLocationsInSameMappedResource()
+ {
+ return this._startLocation.hasMappedLocation() && this._endLocation.hasMappedLocation() && this._startLocation.displaySourceCode === this._endLocation.displaySourceCode;
+ }
+
+ _sourceCodeLocationChanged(event)
+ {
+ this.dispatchEventToListeners(WebInspector.SourceCodeLocation.Event.RangeChanged);
+ }
+};
+
+WebInspector.SourceCodeTextRange.Event = {
+ RangeChanged: "source-code-text-range-range-changed"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodeTimeline.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodeTimeline.js
new file mode 100644
index 000000000..f1699c4df
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodeTimeline.js
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCodeTimeline = class SourceCodeTimeline extends WebInspector.Timeline
+{
+ constructor(sourceCode, sourceCodeLocation, recordType, recordEventType)
+ {
+ super();
+
+ console.assert(sourceCode);
+ console.assert(!sourceCodeLocation || sourceCodeLocation.sourceCode === sourceCode);
+ console.assert(recordType);
+
+ this._sourceCode = sourceCode;
+ this._sourceCodeLocation = sourceCodeLocation || null;
+ this._recordType = recordType;
+ this._recordEventType = recordEventType || null;
+ }
+
+ // Public
+
+ get sourceCode() { return this._sourceCode; }
+ get sourceCodeLocation() { return this._sourceCodeLocation; }
+ get recordType() { return this._recordType; }
+ get recordEventType() { return this._recordEventType; }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.SourceCodeTimeline.SourceCodeURLCookieKey] = this._sourceCode.url ? this._sourceCode.url.hash : null;
+ cookie[WebInspector.SourceCodeTimeline.SourceCodeLocationLineCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.lineNumber : null;
+ cookie[WebInspector.SourceCodeTimeline.SourceCodeLocationColumnCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.columnNumber : null;
+ cookie[WebInspector.SourceCodeTimeline.RecordTypeCookieKey] = this._recordType || null;
+ cookie[WebInspector.SourceCodeTimeline.RecordEventTypeCookieKey] = this._recordEventType || null;
+ }
+};
+
+WebInspector.SourceCodeTimeline.TypeIdentifier = "source-code-timeline";
+WebInspector.SourceCodeTimeline.SourceCodeURLCookieKey = "source-code-timeline-source-code-url";
+WebInspector.SourceCodeTimeline.SourceCodeLocationLineCookieKey = "source-code-timeline-source-code-location-line";
+WebInspector.SourceCodeTimeline.SourceCodeLocationColumnCookieKey = "source-code-timeline-source-code-location-column";
+WebInspector.SourceCodeTimeline.SourceCodeURLCookieKey = "source-code-timeline-source-code-url";
+WebInspector.SourceCodeTimeline.RecordTypeCookieKey = "source-code-timeline-record-type";
+WebInspector.SourceCodeTimeline.RecordEventTypeCookieKey = "source-code-timeline-record-event-type";
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceMap.js b/Source/WebInspectorUI/UserInterface/Models/SourceMap.js
new file mode 100644
index 000000000..1824421bc
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceMap.js
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceMap = class SourceMap extends WebInspector.Object
+{
+ constructor(sourceMappingURL, payload, originalSourceCode)
+ {
+ super();
+
+ if (!WebInspector.SourceMap._base64Map) {
+ var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ WebInspector.SourceMap._base64Map = {};
+ for (var i = 0; i < base64Digits.length; ++i)
+ WebInspector.SourceMap._base64Map[base64Digits.charAt(i)] = i;
+ }
+
+ this._originalSourceCode = originalSourceCode || null;
+ this._sourceMapResources = {};
+ this._sourceMapResourcesList = [];
+
+ this._sourceMappingURL = sourceMappingURL;
+ this._reverseMappingsBySourceURL = {};
+ this._mappings = [];
+ this._sources = {};
+ this._sourceRoot = null;
+ this._sourceContentByURL = {};
+ this._parseMappingPayload(payload);
+ }
+
+ // Public
+
+ get originalSourceCode()
+ {
+ return this._originalSourceCode;
+ }
+
+ get sourceMappingBasePathURLComponents()
+ {
+ if (this._sourceMappingURLBasePathComponents)
+ return this._sourceMappingURLBasePathComponents;
+
+ if (this._sourceRoot) {
+ var baseURLPath = absoluteURL(this._sourceRoot, this._sourceMappingURL);
+ console.assert(baseURLPath);
+ if (baseURLPath) {
+ var urlComponents = parseURL(baseURLPath);
+ if (!/\/$/.test(urlComponents.path))
+ urlComponents.path += "/";
+ this._sourceMappingURLBasePathComponents = urlComponents;
+ return this._sourceMappingURLBasePathComponents;
+ }
+ }
+
+ var urlComponents = parseURL(this._sourceMappingURL);
+
+ // Fallback for JavaScript debuggable named scripts that may not have a complete URL.
+ if (!urlComponents.path)
+ urlComponents.path = this._sourceMappingURL || "";
+
+ urlComponents.path = urlComponents.path.substr(0, urlComponents.path.lastIndexOf(urlComponents.lastPathComponent));
+ urlComponents.lastPathComponent = null;
+ this._sourceMappingURLBasePathComponents = urlComponents;
+ return this._sourceMappingURLBasePathComponents;
+ }
+
+ get resources()
+ {
+ return this._sourceMapResourcesList;
+ }
+
+ addResource(resource)
+ {
+ console.assert(!(resource.url in this._sourceMapResources));
+ this._sourceMapResources[resource.url] = resource;
+ this._sourceMapResourcesList.push(resource);
+ }
+
+ resourceForURL(url)
+ {
+ return this._sourceMapResources[url];
+ }
+
+ sources()
+ {
+ return Object.keys(this._sources);
+ }
+
+ sourceContent(sourceURL)
+ {
+ return this._sourceContentByURL[sourceURL];
+ }
+
+ _parseMappingPayload(mappingPayload)
+ {
+ if (mappingPayload.sections)
+ this._parseSections(mappingPayload.sections);
+ else
+ this._parseMap(mappingPayload, 0, 0);
+ }
+
+ _parseSections(sections)
+ {
+ for (var i = 0; i < sections.length; ++i) {
+ var section = sections[i];
+ this._parseMap(section.map, section.offset.line, section.offset.column);
+ }
+ }
+
+ findEntry(lineNumber, columnNumber)
+ {
+ var first = 0;
+ var count = this._mappings.length;
+ while (count > 1) {
+ var step = count >> 1;
+ var middle = first + step;
+ var mapping = this._mappings[middle];
+ if (lineNumber < mapping[0] || (lineNumber === mapping[0] && columnNumber < mapping[1]))
+ count = step;
+ else {
+ first = middle;
+ count -= step;
+ }
+ }
+ var entry = this._mappings[first];
+ if (!first && entry && (lineNumber < entry[0] || (lineNumber === entry[0] && columnNumber < entry[1])))
+ return null;
+ return entry;
+ }
+
+ findEntryReversed(sourceURL, lineNumber)
+ {
+ var mappings = this._reverseMappingsBySourceURL[sourceURL];
+ for ( ; lineNumber < mappings.length; ++lineNumber) {
+ var mapping = mappings[lineNumber];
+ if (mapping)
+ return mapping;
+ }
+ return this._mappings[0];
+ }
+
+ _parseMap(map, lineNumber, columnNumber)
+ {
+ var sourceIndex = 0;
+ var sourceLineNumber = 0;
+ var sourceColumnNumber = 0;
+ var nameIndex = 0;
+
+ var sources = [];
+ var originalToCanonicalURLMap = {};
+ for (var i = 0; i < map.sources.length; ++i) {
+ var originalSourceURL = map.sources[i];
+ var href = originalSourceURL;
+ if (map.sourceRoot && href.charAt(0) !== "/")
+ href = map.sourceRoot.replace(/\/+$/, "") + "/" + href;
+ var url = absoluteURL(href, this._sourceMappingURL) || href;
+ originalToCanonicalURLMap[originalSourceURL] = url;
+ sources.push(url);
+ this._sources[url] = true;
+
+ if (map.sourcesContent && map.sourcesContent[i])
+ this._sourceContentByURL[url] = map.sourcesContent[i];
+ }
+
+ this._sourceRoot = map.sourceRoot || null;
+
+ var stringCharIterator = new WebInspector.SourceMap.StringCharIterator(map.mappings);
+ var sourceURL = sources[sourceIndex];
+
+ while (true) {
+ if (stringCharIterator.peek() === ",")
+ stringCharIterator.next();
+ else {
+ while (stringCharIterator.peek() === ";") {
+ lineNumber += 1;
+ columnNumber = 0;
+ stringCharIterator.next();
+ }
+ if (!stringCharIterator.hasNext())
+ break;
+ }
+
+ columnNumber += this._decodeVLQ(stringCharIterator);
+ if (this._isSeparator(stringCharIterator.peek())) {
+ this._mappings.push([lineNumber, columnNumber]);
+ continue;
+ }
+
+ var sourceIndexDelta = this._decodeVLQ(stringCharIterator);
+ if (sourceIndexDelta) {
+ sourceIndex += sourceIndexDelta;
+ sourceURL = sources[sourceIndex];
+ }
+ sourceLineNumber += this._decodeVLQ(stringCharIterator);
+ sourceColumnNumber += this._decodeVLQ(stringCharIterator);
+ if (!this._isSeparator(stringCharIterator.peek()))
+ nameIndex += this._decodeVLQ(stringCharIterator);
+
+ this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
+ }
+
+ for (var i = 0; i < this._mappings.length; ++i) {
+ var mapping = this._mappings[i];
+ var url = mapping[2];
+ if (!url)
+ continue;
+ if (!this._reverseMappingsBySourceURL[url])
+ this._reverseMappingsBySourceURL[url] = [];
+ var reverseMappings = this._reverseMappingsBySourceURL[url];
+ var sourceLine = mapping[3];
+ if (!reverseMappings[sourceLine])
+ reverseMappings[sourceLine] = [mapping[0], mapping[1]];
+ }
+ }
+
+ _isSeparator(char)
+ {
+ return char === "," || char === ";";
+ }
+
+ _decodeVLQ(stringCharIterator)
+ {
+ // Read unsigned value.
+ var result = 0;
+ var shift = 0;
+ do {
+ var digit = WebInspector.SourceMap._base64Map[stringCharIterator.next()];
+ result += (digit & WebInspector.SourceMap.VLQ_BASE_MASK) << shift;
+ shift += WebInspector.SourceMap.VLQ_BASE_SHIFT;
+ } while (digit & WebInspector.SourceMap.VLQ_CONTINUATION_MASK);
+
+ // Fix the sign.
+ var negative = result & 1;
+ result >>= 1;
+ return negative ? -result : result;
+ }
+};
+
+WebInspector.SourceMap.VLQ_BASE_SHIFT = 5;
+WebInspector.SourceMap.VLQ_BASE_MASK = (1 << 5) - 1;
+WebInspector.SourceMap.VLQ_CONTINUATION_MASK = 1 << 5;
+
+WebInspector.SourceMap.StringCharIterator = class StringCharIterator
+{
+ constructor(string)
+ {
+ this._string = string;
+ this._position = 0;
+ }
+
+ next()
+ {
+ return this._string.charAt(this._position++);
+ }
+
+ peek()
+ {
+ return this._string.charAt(this._position);
+ }
+
+ hasNext()
+ {
+ return this._position < this._string.length;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceMapResource.js b/Source/WebInspectorUI/UserInterface/Models/SourceMapResource.js
new file mode 100644
index 000000000..e7a17cbdc
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceMapResource.js
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceMapResource = class SourceMapResource extends WebInspector.Resource
+{
+ constructor(url, sourceMap)
+ {
+ super(url, null);
+
+ console.assert(url);
+ console.assert(sourceMap);
+
+ this._sourceMap = sourceMap;
+
+ var inheritedMIMEType = this._sourceMap.originalSourceCode instanceof WebInspector.Resource ? this._sourceMap.originalSourceCode.syntheticMIMEType : null;
+
+ var fileExtension = WebInspector.fileExtensionForURL(url);
+ var fileExtensionMIMEType = WebInspector.mimeTypeForFileExtension(fileExtension, true);
+
+ // FIXME: This is a layering violation. It should use a helper function on the
+ // Resource base-class to set _mimeType and _type.
+ this._mimeType = fileExtensionMIMEType || inheritedMIMEType || "text/javascript";
+ this._type = WebInspector.Resource.typeFromMIMEType(this._mimeType);
+
+ // Mark the resource as loaded so it does not show a spinner in the sidebar.
+ // We will really load the resource the first time content is requested.
+ this.markAsFinished();
+ }
+
+ // Public
+
+ get sourceMap()
+ {
+ return this._sourceMap;
+ }
+
+ get sourceMapDisplaySubpath()
+ {
+ var sourceMappingBasePathURLComponents = this._sourceMap.sourceMappingBasePathURLComponents;
+ var resourceURLComponents = this.urlComponents;
+
+ // Fallback for JavaScript debuggable named scripts that may not have a complete URL.
+ if (!resourceURLComponents.path)
+ resourceURLComponents.path = this.url;
+
+ // Different schemes / hosts. Return the host + path of this resource.
+ if (resourceURLComponents.scheme !== sourceMappingBasePathURLComponents.scheme || resourceURLComponents.host !== sourceMappingBasePathURLComponents.host)
+ return resourceURLComponents.host + (resourceURLComponents.port ? (":" + resourceURLComponents.port) : "") + resourceURLComponents.path;
+
+ // Same host, but not a subpath of the base. This implies a ".." in the relative path.
+ if (!resourceURLComponents.path.startsWith(sourceMappingBasePathURLComponents.path))
+ return relativePath(resourceURLComponents.path, sourceMappingBasePathURLComponents.path);
+
+ // Same host. Just a subpath of the base.
+ return resourceURLComponents.path.substring(sourceMappingBasePathURLComponents.path.length, resourceURLComponents.length);
+ }
+
+ requestContentFromBackend(callback)
+ {
+ // Revert the markAsFinished that was done in the constructor.
+ this.revertMarkAsFinished();
+
+ var inlineContent = this._sourceMap.sourceContent(this.url);
+ if (inlineContent) {
+ // Force inline content to be asynchronous to match the expected load pattern.
+ // FIXME: We don't know the MIME-type for inline content. Guess by analyzing the content?
+ // Returns a promise.
+ return sourceMapResourceLoaded.call(this, {content: inlineContent, mimeType: this.mimeType, statusCode: 200});
+ }
+
+ function sourceMapResourceNotAvailable(error, content, mimeType, statusCode)
+ {
+ this.markAsFailed();
+ return Promise.resolve({
+ error: WebInspector.UIString("An error occurred trying to load the resource."),
+ content,
+ mimeType,
+ statusCode
+ });
+ }
+
+ function sourceMapResourceLoadError(error)
+ {
+ // There was an error calling NetworkAgent.loadResource.
+ console.error(error || "There was an unknown error calling NetworkAgent.loadResource.");
+ this.markAsFailed();
+ return Promise.resolve({error: WebInspector.UIString("An error occurred trying to load the resource.")});
+ }
+
+ function sourceMapResourceLoaded(parameters)
+ {
+ var {error, content, mimeType, statusCode} = parameters;
+
+ var base64encoded = false;
+
+ if (statusCode >= 400 || error)
+ return sourceMapResourceNotAvailable(error, content, mimeType, statusCode);
+
+ // FIXME: Add support for picking the best MIME-type. Right now the file extension is the best bet.
+ // The constructor set MIME-type based on the file extension and we ignore mimeType here.
+
+ this.markAsFinished();
+
+ return Promise.resolve({
+ content,
+ mimeType,
+ base64encoded,
+ statusCode
+ });
+ }
+
+ // COMPATIBILITY (iOS 7): Network.loadResource did not exist.
+ // Also, JavaScript Debuggable with SourceMaps that do not have inlined content may reach this.
+ if (!window.NetworkAgent || !NetworkAgent.loadResource)
+ return sourceMapResourceLoadError.call(this);
+
+ var frameIdentifier = null;
+ if (this._sourceMap.originalSourceCode instanceof WebInspector.Resource && this._sourceMap.originalSourceCode.parentFrame)
+ frameIdentifier = this._sourceMap.originalSourceCode.parentFrame.id;
+
+ if (!frameIdentifier)
+ frameIdentifier = WebInspector.frameResourceManager.mainFrame.id;
+
+ return NetworkAgent.loadResource(frameIdentifier, this.url).then(sourceMapResourceLoaded.bind(this)).catch(sourceMapResourceLoadError.bind(this));
+ }
+
+ createSourceCodeLocation(lineNumber, columnNumber)
+ {
+ // SourceCodeLocations are always constructed with raw resources and raw locations. Lookup the raw location.
+ var entry = this._sourceMap.findEntryReversed(this.url, lineNumber);
+ var rawLineNumber = entry[0];
+ var rawColumnNumber = entry[1];
+
+ // If the raw location is an inline script we need to include that offset.
+ var originalSourceCode = this._sourceMap.originalSourceCode;
+ if (originalSourceCode instanceof WebInspector.Script) {
+ if (rawLineNumber === 0)
+ rawColumnNumber += originalSourceCode.range.startColumn;
+ rawLineNumber += originalSourceCode.range.startLine;
+ }
+
+ // Create the SourceCodeLocation and since we already know the the mapped location set it directly.
+ var location = originalSourceCode.createSourceCodeLocation(rawLineNumber, rawColumnNumber);
+ location._setMappedLocation(this, lineNumber, columnNumber);
+ return location;
+ }
+
+ createSourceCodeTextRange(textRange)
+ {
+ // SourceCodeTextRanges are always constructed with raw resources and raw locations.
+ // However, we can provide the most accurate mapped locations in construction.
+ var startSourceCodeLocation = this.createSourceCodeLocation(textRange.startLine, textRange.startColumn);
+ var endSourceCodeLocation = this.createSourceCodeLocation(textRange.endLine, textRange.endColumn);
+ return new WebInspector.SourceCodeTextRange(this._sourceMap.originalSourceCode, startSourceCodeLocation, endSourceCodeLocation);
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/StackTrace.js b/Source/WebInspectorUI/UserInterface/Models/StackTrace.js
new file mode 100644
index 000000000..89acfc872
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/StackTrace.js
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.StackTrace = class StackTrace extends WebInspector.Object
+{
+ constructor(callFrames, topCallFrameIsBoundary, truncated, parentStackTrace)
+ {
+ super();
+
+ console.assert(callFrames && callFrames.every((callFrame) => callFrame instanceof WebInspector.CallFrame));
+
+ this._callFrames = callFrames;
+ this._topCallFrameIsBoundary = topCallFrameIsBoundary || false;
+ this._truncated = truncated || false;
+ this._parentStackTrace = parentStackTrace || null;
+ }
+
+ // Static
+
+ static fromPayload(target, payload)
+ {
+ let result = null;
+ let previousStackTrace = null;
+
+ while (payload) {
+ let callFrames = payload.callFrames.map((x) => WebInspector.CallFrame.fromPayload(target, x));
+ let stackTrace = new WebInspector.StackTrace(callFrames, payload.topCallFrameIsBoundary, payload.truncated);
+ if (!result)
+ result = stackTrace;
+ if (previousStackTrace)
+ previousStackTrace._parentStackTrace = stackTrace;
+
+ previousStackTrace = stackTrace;
+ payload = payload.parentStackTrace;
+ }
+
+ return result;
+ }
+
+ static fromString(target, stack)
+ {
+ let callFrames = WebInspector.StackTrace._parseStackTrace(stack);
+ return WebInspector.StackTrace.fromPayload(target, {callFrames});
+ }
+
+ // May produce false negatives; must not produce any false positives.
+ // It may return false on a valid stack trace, but it will never return true on an invalid stack trace.
+ static isLikelyStackTrace(stack)
+ {
+ // This function runs for every logged string. It penalizes the performance.
+ // As most logged strings are not stack traces, exit as early as possible.
+ const smallestPossibleStackTraceLength = "http://a.bc/:9:1".length;
+ if (stack.length < smallestPossibleStackTraceLength.length * 2)
+ return false;
+
+ const approximateStackLengthOf50Items = 5000;
+ if (stack.length > approximateStackLengthOf50Items)
+ return false;
+
+ if (/^[^a-z$_]/i.test(stack[0]))
+ return false;
+
+ const reasonablyLongLineLength = 500;
+ const reasonablyLongNativeMethodLength = 120;
+ const stackTraceLine = `(.{1,${reasonablyLongLineLength}}:\\d+:\\d+|eval code|.{1,${reasonablyLongNativeMethodLength}}@\\[native code\\])`;
+ const stackTrace = new RegExp(`^${stackTraceLine}(\\n${stackTraceLine})*$`, "g");
+
+ return stackTrace.test(stack);
+ }
+
+ static _parseStackTrace(stack)
+ {
+ var lines = stack.split(/\n/g);
+ var result = [];
+
+ for (var line of lines) {
+ var functionName = "";
+ var url = "";
+ var lineNumber = 0;
+ var columnNumber = 0;
+ var atIndex = line.indexOf("@");
+
+ if (atIndex !== -1) {
+ functionName = line.slice(0, atIndex);
+ ({url, lineNumber, columnNumber} = WebInspector.StackTrace._parseLocation(line.slice(atIndex + 1)));
+ } else if (line.includes("/"))
+ ({url, lineNumber, columnNumber} = WebInspector.StackTrace._parseLocation(line));
+ else
+ functionName = line;
+
+ result.push({functionName, url, lineNumber, columnNumber});
+ }
+
+ return result;
+ }
+
+ static _parseLocation(locationString)
+ {
+ var result = {url: "", lineNumber: 0, columnNumber: 0};
+ var locationRegEx = /(.+?)(?::(\d+)(?::(\d+))?)?$/;
+ var matched = locationString.match(locationRegEx);
+
+ if (!matched)
+ return result;
+
+ result.url = matched[1];
+
+ if (matched[2])
+ result.lineNumber = parseInt(matched[2]);
+
+ if (matched[3])
+ result.columnNumber = parseInt(matched[3]);
+
+ return result;
+ }
+
+ // Public
+
+ get callFrames()
+ {
+ return this._callFrames;
+ }
+
+ get firstNonNativeCallFrame()
+ {
+ for (let frame of this._callFrames) {
+ if (!frame.nativeCode)
+ return frame;
+ }
+
+ return null;
+ }
+
+ get firstNonNativeNonAnonymousCallFrame()
+ {
+ for (let frame of this._callFrames) {
+ if (frame.nativeCode)
+ continue;
+ if (frame.sourceCodeLocation) {
+ let sourceCode = frame.sourceCodeLocation.sourceCode;
+ if (sourceCode instanceof WebInspector.Script && sourceCode.anonymous)
+ continue;
+ }
+ return frame;
+ }
+
+ return null;
+ }
+
+ get topCallFrameIsBoundary() { return this._topCallFrameIsBoundary; }
+ get truncated() { return this._truncated; }
+ get parentStackTrace() { return this._parentStackTrace; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/StructureDescription.js b/Source/WebInspectorUI/UserInterface/Models/StructureDescription.js
new file mode 100644
index 000000000..097230ff1
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/StructureDescription.js
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.StructureDescription = class StructureDescription extends WebInspector.Object
+{
+ constructor(fields, optionalFields, constructorName, prototypeStructure, imprecise)
+ {
+ super();
+
+ console.assert(!fields || fields.every((x) => typeof x === "string"));
+ console.assert(!optionalFields || optionalFields.every((x) => typeof x === "string"));
+ console.assert(!constructorName || typeof constructorName === "string");
+ console.assert(!prototypeStructure || prototypeStructure instanceof WebInspector.StructureDescription);
+
+ this._fields = fields || null;
+ this._optionalFields = optionalFields || null;
+ this._constructorName = constructorName || "";
+ this._prototypeStructure = prototypeStructure || null;
+ this._imprecise = imprecise || false;
+ }
+
+ // Static
+
+ // Runtime.StructureDescription.
+ static fromPayload(payload)
+ {
+ if (payload.prototypeStructure)
+ payload.prototypeStructure = WebInspector.StructureDescription.fromPayload(payload.prototypeStructure);
+
+ return new WebInspector.StructureDescription(payload.fields, payload.optionalFields, payload.constructorName, payload.prototypeStructure, payload.imprecise);
+ }
+
+ // Public
+
+ get fields() { return this._fields; }
+ get optionalFields() { return this._optionalFields; }
+ get constructorName() { return this._constructorName; }
+ get prototypeStructure() { return this._prototypeStructure; }
+ get imprecise() { return this._imprecise; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/TextMarker.js b/Source/WebInspectorUI/UserInterface/Models/TextMarker.js
new file mode 100644
index 000000000..f7480ef58
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TextMarker.js
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TextMarker = class TextMarker extends WebInspector.Object
+{
+ constructor(codeMirrorTextMarker, type)
+ {
+ super();
+
+ this._codeMirrorTextMarker = codeMirrorTextMarker;
+ codeMirrorTextMarker.__webInspectorTextMarker = this;
+
+ this._type = type || WebInspector.TextMarker.Type.Plain;
+ }
+
+ // Static
+
+ static textMarkerForCodeMirrorTextMarker(codeMirrorTextMarker)
+ {
+ return codeMirrorTextMarker.__webInspectorTextMarker || new WebInspector.TextMarker(codeMirrorTextMarker);
+ }
+
+ // Public
+
+ get codeMirrorTextMarker()
+ {
+ return this._codeMirrorTextMarker;
+ }
+
+ get type()
+ {
+ return this._type;
+ }
+
+ get range()
+ {
+ var range = this._codeMirrorTextMarker.find();
+ if (!range)
+ return null;
+ return new WebInspector.TextRange(range.from.line, range.from.ch, range.to.line, range.to.ch);
+ }
+
+ get rects()
+ {
+ var range = this._codeMirrorTextMarker.find();
+ if (!range)
+ return WebInspector.Rect.ZERO_RECT;
+ return this._codeMirrorTextMarker.doc.cm.rectsForRange({
+ start: range.from,
+ end: range.to
+ });
+ }
+
+ clear()
+ {
+ this._codeMirrorTextMarker.clear();
+ }
+};
+
+WebInspector.TextMarker.Type = {
+ Color: "text-marker-type-color",
+ Gradient: "text-marker-type-gradient",
+ Plain: "text-marker-type-plain",
+ CubicBezier: "text-marker-type-cubic-bezier",
+ Spring: "text-marker-type-spring",
+ Variable: "text-marker-type-variable",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/TextRange.js b/Source/WebInspectorUI/UserInterface/Models/TextRange.js
new file mode 100644
index 000000000..64fe72147
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TextRange.js
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TextRange = class TextRange extends WebInspector.Object
+{
+ constructor(startLineOrStartOffset, startColumnOrEndOffset, endLine, endColumn)
+ {
+ super();
+
+ if (arguments.length === 4) {
+ console.assert(startLineOrStartOffset <= endLine);
+ console.assert(startLineOrStartOffset !== endLine || startColumnOrEndOffset <= endColumn);
+
+ this._startLine = typeof startLineOrStartOffset === "number" ? startLineOrStartOffset : NaN;
+ this._startColumn = typeof startColumnOrEndOffset === "number" ? startColumnOrEndOffset : NaN;
+ this._endLine = typeof endLine === "number" ? endLine : NaN;
+ this._endColumn = typeof endColumn === "number" ? endColumn : NaN;
+
+ this._startOffset = NaN;
+ this._endOffset = NaN;
+ } else if (arguments.length === 2) {
+ console.assert(startLineOrStartOffset <= startColumnOrEndOffset);
+
+ this._startOffset = typeof startLineOrStartOffset === "number" ? startLineOrStartOffset : NaN;
+ this._endOffset = typeof startColumnOrEndOffset === "number" ? startColumnOrEndOffset : NaN;
+
+ this._startLine = NaN;
+ this._startColumn = NaN;
+ this._endLine = NaN;
+ this._endColumn = NaN;
+ }
+ }
+
+ // Public
+
+ get startLine() { return this._startLine; }
+ get startColumn() { return this._startColumn; }
+ get endLine() { return this._endLine; }
+ get endColumn() { return this._endColumn; }
+ get startOffset() { return this._startOffset; }
+ get endOffset() { return this._endOffset; }
+
+ startPosition()
+ {
+ return new WebInspector.SourceCodePosition(this._startLine, this._startColumn);
+ }
+
+ endPosition()
+ {
+ return new WebInspector.SourceCodePosition(this._endLine, this._endColumn);
+ }
+
+ resolveOffsets(text)
+ {
+ console.assert(typeof text === "string");
+ if (typeof text !== "string")
+ return;
+
+ console.assert(!isNaN(this._startLine));
+ console.assert(!isNaN(this._startColumn));
+ console.assert(!isNaN(this._endLine));
+ console.assert(!isNaN(this._endColumn));
+ if (isNaN(this._startLine) || isNaN(this._startColumn) || isNaN(this._endLine) || isNaN(this._endColumn))
+ return;
+
+ var lastNewLineOffset = 0;
+ for (var i = 0; i < this._startLine; ++i)
+ lastNewLineOffset = text.indexOf("\n", lastNewLineOffset) + 1;
+
+ this._startOffset = lastNewLineOffset + this._startColumn;
+
+ for (var i = this._startLine; i < this._endLine; ++i)
+ lastNewLineOffset = text.indexOf("\n", lastNewLineOffset) + 1;
+
+ this._endOffset = lastNewLineOffset + this._endColumn;
+ }
+
+ contains(line, column)
+ {
+ console.assert(!isNaN(this._startLine), "TextRange needs line/column data");
+
+ if (line < this._startLine || line > this._endLine)
+ return false;
+ if (line === this._startLine && column < this._startColumn)
+ return false;
+ if (line === this._endLine && column > this._endColumn)
+ return false;
+
+ return true;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Timeline.js b/Source/WebInspectorUI/UserInterface/Models/Timeline.js
new file mode 100644
index 000000000..857e8a37c
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Timeline.js
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Timeline = class Timeline extends WebInspector.Object
+{
+ constructor(type)
+ {
+ super();
+
+ this._type = type;
+
+ this.reset(true);
+ }
+
+ // Static
+
+ static create(type)
+ {
+ if (type === WebInspector.TimelineRecord.Type.Network)
+ return new WebInspector.NetworkTimeline(type);
+
+ if (type === WebInspector.TimelineRecord.Type.Memory)
+ return new WebInspector.MemoryTimeline(type);
+
+ return new WebInspector.Timeline(type);
+ }
+
+ // Public
+
+ get type() { return this._type; }
+ get startTime() { return this._startTime; }
+ get endTime() { return this._endTime; }
+ get records() { return this._records; }
+
+ reset(suppressEvents)
+ {
+ this._records = [];
+ this._startTime = NaN;
+ this._endTime = NaN;
+
+ if (!suppressEvents) {
+ this.dispatchEventToListeners(WebInspector.Timeline.Event.TimesUpdated);
+ this.dispatchEventToListeners(WebInspector.Timeline.Event.Reset);
+ }
+ }
+
+ addRecord(record)
+ {
+ if (record.updatesDynamically)
+ record.addEventListener(WebInspector.TimelineRecord.Event.Updated, this._recordUpdated, this);
+
+ this._records.push(record);
+
+ this._updateTimesIfNeeded(record);
+
+ this.dispatchEventToListeners(WebInspector.Timeline.Event.RecordAdded, {record});
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.Timeline.TimelineTypeCookieKey] = this._type;
+ }
+
+ refresh()
+ {
+ this.dispatchEventToListeners(WebInspector.Timeline.Event.Refreshed);
+ }
+
+ recordsInTimeRange(startTime, endTime, includeRecordBeforeStart)
+ {
+ let lowerIndex = this._records.lowerBound(startTime, (time, record) => time - record.timestamp);
+ let upperIndex = this._records.upperBound(endTime, (time, record) => time - record.timestamp);
+
+ // Include the record right before the start time.
+ if (includeRecordBeforeStart && lowerIndex > 0)
+ lowerIndex--;
+
+ return this._records.slice(lowerIndex, upperIndex);
+ }
+
+ // Private
+
+ _updateTimesIfNeeded(record)
+ {
+ var changed = false;
+
+ if (isNaN(this._startTime) || record.startTime < this._startTime) {
+ this._startTime = record.startTime;
+ changed = true;
+ }
+
+ if (isNaN(this._endTime) || this._endTime < record.endTime) {
+ this._endTime = record.endTime;
+ changed = true;
+ }
+
+ if (changed)
+ this.dispatchEventToListeners(WebInspector.Timeline.Event.TimesUpdated);
+ }
+
+ _recordUpdated(event)
+ {
+ this._updateTimesIfNeeded(event.target);
+ }
+};
+
+WebInspector.Timeline.Event = {
+ Reset: "timeline-reset",
+ RecordAdded: "timeline-record-added",
+ TimesUpdated: "timeline-times-updated",
+ Refreshed: "timeline-refreshed",
+};
+
+WebInspector.Timeline.TimelineTypeCookieKey = "timeline-type";
diff --git a/Source/WebInspectorUI/UserInterface/Models/TimelineMarker.js b/Source/WebInspectorUI/UserInterface/Models/TimelineMarker.js
new file mode 100644
index 000000000..dee038cb0
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TimelineMarker.js
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TimelineMarker = class TimelineMarker extends WebInspector.Object
+{
+ constructor(time, type, details)
+ {
+ super();
+
+ console.assert(type);
+
+ this._time = time || 0;
+ this._type = type;
+ this._details = details || null;
+ }
+
+ // Public
+
+ get time()
+ {
+ return this._time;
+ }
+
+ set time(x)
+ {
+ console.assert(typeof x === "number", "Time should be a number.");
+
+ x = x || 0;
+
+ if (this._time === x)
+ return;
+
+ this._time = x;
+
+ this.dispatchEventToListeners(WebInspector.TimelineMarker.Event.TimeChanged);
+ }
+
+ get type()
+ {
+ return this._type;
+ }
+
+ get details()
+ {
+ return this._details;
+ }
+};
+
+WebInspector.TimelineMarker.Event = {
+ TimeChanged: "timeline-marker-time-changed"
+};
+
+WebInspector.TimelineMarker.Type = {
+ CurrentTime: "current-time",
+ LoadEvent: "load-event",
+ DOMContentEvent: "dom-content-event",
+ TimeStamp: "timestamp"
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/TimelineRange.js b/Source/WebInspectorUI/UserInterface/Models/TimelineRange.js
new file mode 100644
index 000000000..60ef60f93
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TimelineRange.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TimelineRange = class TimelineRange extends WebInspector.Object
+{
+ constructor(startValue, endValue)
+ {
+ super();
+
+ this._startValue = startValue;
+ this._endValue = endValue;
+ }
+
+ get startValue() { return this._startValue; }
+ set startValue(x) { this._startValue = x; }
+
+ get endValue() { return this._endValue; }
+ set endValue(x) { this._endValue = x; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js
new file mode 100644
index 000000000..99f443147
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TimelineRecord = class TimelineRecord extends WebInspector.Object
+{
+ constructor(type, startTime, endTime, callFrames, sourceCodeLocation)
+ {
+ super();
+
+ console.assert(type);
+
+ if (type in WebInspector.TimelineRecord.Type)
+ type = WebInspector.TimelineRecord.Type[type];
+
+ this._type = type;
+ this._startTime = startTime || NaN;
+ this._endTime = endTime || NaN;
+ this._callFrames = callFrames || null;
+ this._sourceCodeLocation = sourceCodeLocation || null;
+ this._children = [];
+ }
+
+ // Public
+
+ get type()
+ {
+ return this._type;
+ }
+
+ get startTime()
+ {
+ // Implemented by subclasses if needed.
+ return this._startTime;
+ }
+
+ get activeStartTime()
+ {
+ // Implemented by subclasses if needed.
+ return this._startTime;
+ }
+
+ get endTime()
+ {
+ // Implemented by subclasses if needed.
+ return this._endTime;
+ }
+
+ get duration()
+ {
+ // Use the getters instead of the properties so this works for subclasses that override the getters.
+ return this.endTime - this.startTime;
+ }
+
+ get inactiveDuration()
+ {
+ // Use the getters instead of the properties so this works for subclasses that override the getters.
+ return this.activeStartTime - this.startTime;
+ }
+
+ get activeDuration()
+ {
+ // Use the getters instead of the properties so this works for subclasses that override the getters.
+ return this.endTime - this.activeStartTime;
+ }
+
+ get updatesDynamically()
+ {
+ // Implemented by subclasses if needed.
+ return false;
+ }
+
+ get usesActiveStartTime()
+ {
+ // Implemented by subclasses if needed.
+ return false;
+ }
+
+ get callFrames()
+ {
+ return this._callFrames;
+ }
+
+ get initiatorCallFrame()
+ {
+ if (!this._callFrames || !this._callFrames.length)
+ return null;
+
+ // Return the first non-native code call frame as the initiator.
+ for (var i = 0; i < this._callFrames.length; ++i) {
+ if (this._callFrames[i].nativeCode)
+ continue;
+ return this._callFrames[i];
+ }
+
+ return null;
+ }
+
+ get sourceCodeLocation()
+ {
+ return this._sourceCodeLocation;
+ }
+
+ get parent()
+ {
+ return this._parent;
+ }
+
+ set parent(x)
+ {
+ if (this._parent === x)
+ return;
+
+ this._parent = x;
+ }
+
+ get children()
+ {
+ return this._children;
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.TimelineRecord.SourceCodeURLCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.sourceCode.url ? this._sourceCodeLocation.sourceCode.url.hash : null : null;
+ cookie[WebInspector.TimelineRecord.SourceCodeLocationLineCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.lineNumber : null;
+ cookie[WebInspector.TimelineRecord.SourceCodeLocationColumnCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.columnNumber : null;
+ cookie[WebInspector.TimelineRecord.TypeCookieKey] = this._type || null;
+ }
+};
+
+WebInspector.TimelineRecord.Event = {
+ Updated: "timeline-record-updated"
+};
+
+WebInspector.TimelineRecord.Type = {
+ Network: "timeline-record-type-network",
+ Layout: "timeline-record-type-layout",
+ Script: "timeline-record-type-script",
+ RenderingFrame: "timeline-record-type-rendering-frame",
+ Memory: "timeline-record-type-memory",
+ HeapAllocations: "timeline-record-type-heap-allocations",
+};
+
+WebInspector.TimelineRecord.TypeIdentifier = "timeline-record";
+WebInspector.TimelineRecord.SourceCodeURLCookieKey = "timeline-record-source-code-url";
+WebInspector.TimelineRecord.SourceCodeLocationLineCookieKey = "timeline-record-source-code-location-line";
+WebInspector.TimelineRecord.SourceCodeLocationColumnCookieKey = "timeline-record-source-code-location-column";
+WebInspector.TimelineRecord.TypeCookieKey = "timeline-record-type";
diff --git a/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js b/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js
new file mode 100644
index 000000000..f4cc10295
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TimelineRecording = class TimelineRecording extends WebInspector.Object
+{
+ constructor(identifier, displayName, instruments)
+ {
+ super();
+
+ this._identifier = identifier;
+ this._timelines = new Map;
+ this._displayName = displayName;
+ this._capturing = false;
+ this._readonly = false;
+ this._instruments = instruments || [];
+ this._topDownCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.TopDown);
+ this._bottomUpCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.BottomUp);
+ this._topFunctionsTopDownCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.TopFunctionsTopDown);
+ this._topFunctionsBottomUpCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.TopFunctionsBottomUp);
+
+ for (let type of WebInspector.TimelineManager.availableTimelineTypes()) {
+ let timeline = WebInspector.Timeline.create(type);
+ this._timelines.set(type, timeline);
+ timeline.addEventListener(WebInspector.Timeline.Event.TimesUpdated, this._timelineTimesUpdated, this);
+ }
+
+ // For legacy backends, we compute the elapsed time of records relative to this timestamp.
+ this._legacyFirstRecordedTimestamp = NaN;
+
+ this.reset(true);
+ }
+
+ // Static
+
+ static sourceCodeTimelinesSupported()
+ {
+ return WebInspector.debuggableType === WebInspector.DebuggableType.Web;
+ }
+
+ // Public
+
+ get displayName() { return this._displayName; }
+ get identifier() { return this._identifier; }
+ get timelines() { return this._timelines; }
+ get instruments() { return this._instruments; }
+ get readonly() { return this._readonly; }
+ get startTime() { return this._startTime; }
+ get endTime() { return this._endTime; }
+
+ get topDownCallingContextTree() { return this._topDownCallingContextTree; }
+ get bottomUpCallingContextTree() { return this._bottomUpCallingContextTree; }
+ get topFunctionsTopDownCallingContextTree() { return this._topFunctionsTopDownCallingContextTree; }
+ get topFunctionsBottomUpCallingContextTree() { return this._topFunctionsBottomUpCallingContextTree; }
+
+ start(initiatedByBackend)
+ {
+ console.assert(!this._capturing, "Attempted to start an already started session.");
+ console.assert(!this._readonly, "Attempted to start a readonly session.");
+
+ this._capturing = true;
+
+ for (let instrument of this._instruments)
+ instrument.startInstrumentation(initiatedByBackend);
+ }
+
+ stop(initiatedByBackend)
+ {
+ console.assert(this._capturing, "Attempted to stop an already stopped session.");
+ console.assert(!this._readonly, "Attempted to stop a readonly session.");
+
+ this._capturing = false;
+
+ for (let instrument of this._instruments)
+ instrument.stopInstrumentation(initiatedByBackend);
+ }
+
+ saveIdentityToCookie()
+ {
+ // Do nothing. Timeline recordings are not persisted when the inspector is
+ // re-opened, so do not attempt to restore by identifier or display name.
+ }
+
+ isEmpty()
+ {
+ for (var timeline of this._timelines.values()) {
+ if (timeline.records.length)
+ return false;
+ }
+
+ return true;
+ }
+
+ unloaded()
+ {
+ console.assert(!this.isEmpty(), "Shouldn't unload an empty recording; it should be reused instead.");
+
+ this._readonly = true;
+
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.Unloaded);
+ }
+
+ reset(suppressEvents)
+ {
+ console.assert(!this._readonly, "Can't reset a read-only recording.");
+
+ this._sourceCodeTimelinesMap = new Map;
+ this._eventMarkers = [];
+ this._startTime = NaN;
+ this._endTime = NaN;
+ this._discontinuities = [];
+
+ this._topDownCallingContextTree.reset();
+ this._bottomUpCallingContextTree.reset();
+ this._topFunctionsTopDownCallingContextTree.reset();
+ this._topFunctionsBottomUpCallingContextTree.reset();
+
+ for (var timeline of this._timelines.values())
+ timeline.reset(suppressEvents);
+
+ WebInspector.RenderingFrameTimelineRecord.resetFrameIndex();
+
+ if (!suppressEvents) {
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.Reset);
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.TimesUpdated);
+ }
+ }
+
+ sourceCodeTimelinesForSourceCode(sourceCode)
+ {
+ var timelines = this._sourceCodeTimelinesMap.get(sourceCode);
+ if (!timelines)
+ return [];
+ return [...timelines.values()];
+ }
+
+ timelineForInstrument(instrument)
+ {
+ return this._timelines.get(instrument.timelineRecordType);
+ }
+
+ instrumentForTimeline(timeline)
+ {
+ return this._instruments.find((instrument) => instrument.timelineRecordType === timeline.type);
+ }
+
+ timelineForRecordType(recordType)
+ {
+ return this._timelines.get(recordType);
+ }
+
+ addInstrument(instrument)
+ {
+ console.assert(instrument instanceof WebInspector.Instrument, instrument);
+ console.assert(!this._instruments.includes(instrument), this._instruments, instrument);
+
+ this._instruments.push(instrument);
+
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.InstrumentAdded, {instrument});
+ }
+
+ removeInstrument(instrument)
+ {
+ console.assert(instrument instanceof WebInspector.Instrument, instrument);
+ console.assert(this._instruments.includes(instrument), this._instruments, instrument);
+
+ this._instruments.remove(instrument);
+
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.InstrumentRemoved, {instrument});
+ }
+
+ addEventMarker(marker)
+ {
+ if (!this._capturing)
+ return;
+
+ this._eventMarkers.push(marker);
+
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.MarkerAdded, {marker});
+ }
+
+ addRecord(record)
+ {
+ var timeline = this._timelines.get(record.type);
+ console.assert(timeline, record, this._timelines);
+ if (!timeline)
+ return;
+
+ // Add the record to the global timeline by type.
+ timeline.addRecord(record);
+
+ // Some records don't have source code timelines.
+ if (record.type === WebInspector.TimelineRecord.Type.Network
+ || record.type === WebInspector.TimelineRecord.Type.RenderingFrame
+ || record.type === WebInspector.TimelineRecord.Type.Memory
+ || record.type === WebInspector.TimelineRecord.Type.HeapAllocations)
+ return;
+
+ if (!WebInspector.TimelineRecording.sourceCodeTimelinesSupported())
+ return;
+
+ // Add the record to the source code timelines.
+ var activeMainResource = WebInspector.frameResourceManager.mainFrame.provisionalMainResource || WebInspector.frameResourceManager.mainFrame.mainResource;
+ var sourceCode = record.sourceCodeLocation ? record.sourceCodeLocation.sourceCode : activeMainResource;
+
+ var sourceCodeTimelines = this._sourceCodeTimelinesMap.get(sourceCode);
+ if (!sourceCodeTimelines) {
+ sourceCodeTimelines = new Map;
+ this._sourceCodeTimelinesMap.set(sourceCode, sourceCodeTimelines);
+ }
+
+ var newTimeline = false;
+ var key = this._keyForRecord(record);
+ var sourceCodeTimeline = sourceCodeTimelines.get(key);
+ if (!sourceCodeTimeline) {
+ sourceCodeTimeline = new WebInspector.SourceCodeTimeline(sourceCode, record.sourceCodeLocation, record.type, record.eventType);
+ sourceCodeTimelines.set(key, sourceCodeTimeline);
+ newTimeline = true;
+ }
+
+ sourceCodeTimeline.addRecord(record);
+
+ if (newTimeline)
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.SourceCodeTimelineAdded, {sourceCodeTimeline});
+ }
+
+ addMemoryPressureEvent(memoryPressureEvent)
+ {
+ let memoryTimeline = this._timelines.get(WebInspector.TimelineRecord.Type.Memory);
+ console.assert(memoryTimeline, this._timelines);
+ if (!memoryTimeline)
+ return;
+
+ memoryTimeline.addMemoryPressureEvent(memoryPressureEvent);
+ }
+
+ addDiscontinuity(startTime, endTime)
+ {
+ this._discontinuities.push({startTime, endTime});
+ }
+
+ discontinuitiesInTimeRange(startTime, endTime)
+ {
+ return this._discontinuities.filter((item) => item.startTime < endTime && item.endTime > startTime);
+ }
+
+ addScriptInstrumentForProgrammaticCapture()
+ {
+ for (let instrument of this._instruments) {
+ if (instrument instanceof WebInspector.ScriptInstrument)
+ return;
+ }
+
+ this.addInstrument(new WebInspector.ScriptInstrument);
+
+ let instrumentTypes = this._instruments.map((instrument) => instrument.timelineRecordType);
+ WebInspector.timelineManager.enabledTimelineTypes = instrumentTypes;
+ }
+
+ computeElapsedTime(timestamp)
+ {
+ if (!timestamp || isNaN(timestamp))
+ return NaN;
+
+ // COMPATIBILITY (iOS 8): old backends send timestamps (seconds or milliseconds since the epoch),
+ // rather than seconds elapsed since timeline capturing started. We approximate the latter by
+ // subtracting the start timestamp, as old versions did not use monotonic times.
+ if (WebInspector.TimelineRecording.isLegacy === undefined)
+ WebInspector.TimelineRecording.isLegacy = timestamp > WebInspector.TimelineRecording.TimestampThresholdForLegacyRecordConversion;
+
+ if (!WebInspector.TimelineRecording.isLegacy)
+ return timestamp;
+
+ // If the record's start time is large, but not really large, then it is seconds since epoch
+ // not millseconds since epoch, so convert it to milliseconds.
+ if (timestamp < WebInspector.TimelineRecording.TimestampThresholdForLegacyAssumedMilliseconds)
+ timestamp *= 1000;
+
+ if (isNaN(this._legacyFirstRecordedTimestamp))
+ this._legacyFirstRecordedTimestamp = timestamp;
+
+ // Return seconds since the first recorded value.
+ return (timestamp - this._legacyFirstRecordedTimestamp) / 1000.0;
+ }
+
+ setLegacyBaseTimestamp(timestamp)
+ {
+ console.assert(isNaN(this._legacyFirstRecordedTimestamp));
+
+ if (timestamp < WebInspector.TimelineRecording.TimestampThresholdForLegacyAssumedMilliseconds)
+ timestamp *= 1000;
+
+ this._legacyFirstRecordedTimestamp = timestamp;
+ }
+
+ initializeTimeBoundsIfNecessary(timestamp)
+ {
+ if (isNaN(this._startTime)) {
+ console.assert(isNaN(this._endTime));
+
+ this._startTime = timestamp;
+ this._endTime = timestamp;
+
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.TimesUpdated);
+ }
+ }
+
+ // Private
+
+ _keyForRecord(record)
+ {
+ var key = record.type;
+ if (record instanceof WebInspector.ScriptTimelineRecord || record instanceof WebInspector.LayoutTimelineRecord)
+ key += ":" + record.eventType;
+ if (record instanceof WebInspector.ScriptTimelineRecord && record.eventType === WebInspector.ScriptTimelineRecord.EventType.EventDispatched)
+ key += ":" + record.details;
+ if (record.sourceCodeLocation)
+ key += ":" + record.sourceCodeLocation.lineNumber + ":" + record.sourceCodeLocation.columnNumber;
+ return key;
+ }
+
+ _timelineTimesUpdated(event)
+ {
+ var timeline = event.target;
+ var changed = false;
+
+ if (isNaN(this._startTime) || timeline.startTime < this._startTime) {
+ this._startTime = timeline.startTime;
+ changed = true;
+ }
+
+ if (isNaN(this._endTime) || this._endTime < timeline.endTime) {
+ this._endTime = timeline.endTime;
+ changed = true;
+ }
+
+ if (changed)
+ this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.TimesUpdated);
+ }
+};
+
+WebInspector.TimelineRecording.Event = {
+ Reset: "timeline-recording-reset",
+ Unloaded: "timeline-recording-unloaded",
+ SourceCodeTimelineAdded: "timeline-recording-source-code-timeline-added",
+ InstrumentAdded: "timeline-recording-instrument-added",
+ InstrumentRemoved: "timeline-recording-instrument-removed",
+ TimesUpdated: "timeline-recording-times-updated",
+ MarkerAdded: "timeline-recording-marker-added",
+};
+
+WebInspector.TimelineRecording.isLegacy = undefined;
+WebInspector.TimelineRecording.TimestampThresholdForLegacyRecordConversion = 10000000; // Some value not near zero.
+WebInspector.TimelineRecording.TimestampThresholdForLegacyAssumedMilliseconds = 1420099200000; // Date.parse("Jan 1, 2015"). Milliseconds since epoch.
diff --git a/Source/WebInspectorUI/UserInterface/Models/TypeDescription.js b/Source/WebInspectorUI/UserInterface/Models/TypeDescription.js
new file mode 100644
index 000000000..60a08def6
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TypeDescription.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TypeDescription = class TypeDescription extends WebInspector.Object
+{
+ constructor(leastCommonAncestor, typeSet, structures, valid, truncated)
+ {
+ super();
+
+ console.assert(!leastCommonAncestor || typeof leastCommonAncestor === "string");
+ console.assert(!typeSet || typeSet instanceof WebInspector.TypeSet);
+ console.assert(!structures || structures.every((x) => x instanceof WebInspector.StructureDescription));
+
+ this._leastCommonAncestor = leastCommonAncestor || null;
+ this._typeSet = typeSet || null;
+ this._structures = structures || null;
+ this._valid = valid || false;
+ this._truncated = truncated || false;
+ }
+
+ // Static
+
+ // Runtime.TypeDescription.
+ static fromPayload(payload)
+ {
+ var typeSet = undefined;
+ if (payload.typeSet)
+ typeSet = WebInspector.TypeSet.fromPayload(payload.typeSet);
+
+ var structures = undefined;
+ if (payload.structures)
+ structures = payload.structures.map(WebInspector.StructureDescription.fromPayload);
+
+ return new WebInspector.TypeDescription(payload.leastCommonAncestor, typeSet, structures, payload.isValid, payload.isTruncated);
+ }
+
+ // Public
+
+ get leastCommonAncestor() { return this._leastCommonAncestor; }
+ get typeSet() { return this._typeSet; }
+ get structures() { return this._structures; }
+ get valid() { return this._valid; }
+ get truncated() { return this._truncated; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/TypeSet.js b/Source/WebInspectorUI/UserInterface/Models/TypeSet.js
new file mode 100644
index 000000000..ad5b430c9
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/TypeSet.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) Saam Barati.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TypeSet = class TypeSet extends WebInspector.Object
+{
+ constructor(typeSet)
+ {
+ super();
+
+ console.assert(typeSet);
+
+ var bitString = 0x0;
+ if (typeSet.isFunction)
+ bitString |= WebInspector.TypeSet.TypeBit.Function;
+ if (typeSet.isUndefined)
+ bitString |= WebInspector.TypeSet.TypeBit.Undefined;
+ if (typeSet.isNull)
+ bitString |= WebInspector.TypeSet.TypeBit.Null;
+ if (typeSet.isBoolean)
+ bitString |= WebInspector.TypeSet.TypeBit.Boolean;
+ if (typeSet.isInteger)
+ bitString |= WebInspector.TypeSet.TypeBit.Integer;
+ if (typeSet.isNumber)
+ bitString |= WebInspector.TypeSet.TypeBit.Number;
+ if (typeSet.isString)
+ bitString |= WebInspector.TypeSet.TypeBit.String;
+ if (typeSet.isObject)
+ bitString |= WebInspector.TypeSet.TypeBit.Object;
+ if (typeSet.isSymbol)
+ bitString |= WebInspector.TypeSet.TypeBit.Symbol;
+ console.assert(bitString);
+
+ this._typeSet = typeSet;
+ this._bitString = bitString;
+ this._primitiveTypeNames = null;
+ }
+
+ // Static
+
+ static fromPayload(payload)
+ {
+ return new WebInspector.TypeSet(payload);
+ }
+
+ // Public
+
+ isContainedIn(test)
+ {
+ // This function checks if types in bitString are contained in the types described by the 'test' bitstring. (i.e we haven't seen more types than 'test').
+ // We have seen fewer or equal number of types as 'test' if ANDing bitString with test doesn't zero out any of our bits.
+
+ // For example:
+
+ // 0b0110 (bitString)
+ // 0b1111 (test)
+ // ------ (AND)
+ // 0b0110 == bitString
+
+ // 0b0110 (bitString)
+ // 0b0010 (test)
+ // ------ (AND)
+ // 0b0010 != bitString
+
+ return this._bitString && (this._bitString & test) === this._bitString;
+ }
+
+ get primitiveTypeNames()
+ {
+ if (this._primitiveTypeNames)
+ return this._primitiveTypeNames;
+
+ this._primitiveTypeNames = [];
+ var typeSet = this._typeSet;
+ if (typeSet.isUndefined)
+ this._primitiveTypeNames.push("Undefined");
+ if (typeSet.isNull)
+ this._primitiveTypeNames.push("Null");
+ if (typeSet.isBoolean)
+ this._primitiveTypeNames.push("Boolean");
+ if (typeSet.isString)
+ this._primitiveTypeNames.push("String");
+ if (typeSet.isSymbol)
+ this._primitiveTypeNames.push("Symbol");
+
+ // It's implied that type Integer is contained in type Number. Don't put
+ // both 'Integer' and 'Number' into the set because this could imply that
+ // Number means to Double instead of Double|Integer.
+ if (typeSet.isNumber)
+ this._primitiveTypeNames.push("Number");
+ else if (typeSet.isInteger)
+ this._primitiveTypeNames.push("Integer");
+
+ return this._primitiveTypeNames;
+ }
+};
+
+WebInspector.TypeSet.TypeBit = {
+ "Function" : 0x1,
+ "Undefined" : 0x2,
+ "Null" : 0x4,
+ "Boolean" : 0x8,
+ "Integer" : 0x10,
+ "Number" : 0x20,
+ "String" : 0x40,
+ "Object" : 0x80,
+ "Symbol" : 0x100
+};
+
+WebInspector.TypeSet.NullOrUndefinedTypeBits = WebInspector.TypeSet.TypeBit.Null | WebInspector.TypeSet.TypeBit.Undefined;
diff --git a/Source/WebInspectorUI/UserInterface/Models/WrappedPromise.js b/Source/WebInspectorUI/UserInterface/Models/WrappedPromise.js
new file mode 100644
index 000000000..a42253d26
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/WrappedPromise.js
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.WrappedPromise = class WrappedPromise
+{
+ constructor(work)
+ {
+ this._settled = false;
+ this._promise = new Promise((resolve, reject) => {
+ this._resolveCallback = resolve;
+ this._rejectCallback = reject;
+
+ // Allow work to resolve or reject the promise by shimming our
+ // internal callbacks. This ensures that this._settled gets set properly.
+ if (work && typeof work === "function")
+ return work(this.resolve.bind(this), this.reject.bind(this));
+ });
+ }
+
+ // Public
+
+ get settled()
+ {
+ return this._settled;
+ }
+
+ get promise()
+ {
+ return this._promise;
+ }
+
+ resolve(value)
+ {
+ if (this._settled)
+ throw new Error("Promise is already settled, cannot call resolve().");
+
+ this._settled = true;
+ this._resolveCallback(value);
+ }
+
+ reject(value)
+ {
+ if (this._settled)
+ throw new Error("Promise is already settled, cannot call reject().");
+
+ this._settled = true;
+ this._rejectCallback(value);
+ }
+};